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.transport;
45
46 import static java.nio.charset.StandardCharsets.UTF_8;
47 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_ATOMIC;
48 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_DELETE_REFS;
49 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
50 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
51 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
52 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
53 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
54 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
55 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
56 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_ERROR;
57 import static org.eclipse.jgit.transport.SideBandOutputStream.CH_PROGRESS;
58 import static org.eclipse.jgit.transport.SideBandOutputStream.MAX_BUF;
59
60 import java.io.EOFException;
61 import java.io.IOException;
62 import java.io.InputStream;
63 import java.io.OutputStream;
64 import java.text.MessageFormat;
65 import java.util.ArrayList;
66 import java.util.Collections;
67 import java.util.HashSet;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Set;
71 import java.util.concurrent.TimeUnit;
72
73 import org.eclipse.jgit.annotations.Nullable;
74 import org.eclipse.jgit.errors.InvalidObjectIdException;
75 import org.eclipse.jgit.errors.MissingObjectException;
76 import org.eclipse.jgit.errors.PackProtocolException;
77 import org.eclipse.jgit.errors.TooLargePackException;
78 import org.eclipse.jgit.internal.JGitText;
79 import org.eclipse.jgit.internal.storage.file.PackLock;
80 import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
81 import org.eclipse.jgit.lib.AnyObjectId;
82 import org.eclipse.jgit.lib.BatchRefUpdate;
83 import org.eclipse.jgit.lib.Config;
84 import org.eclipse.jgit.lib.Constants;
85 import org.eclipse.jgit.lib.GitmoduleEntry;
86 import org.eclipse.jgit.lib.NullProgressMonitor;
87 import org.eclipse.jgit.lib.ObjectChecker;
88 import org.eclipse.jgit.lib.ObjectDatabase;
89 import org.eclipse.jgit.lib.ObjectId;
90 import org.eclipse.jgit.lib.ObjectIdSubclassMap;
91 import org.eclipse.jgit.lib.ObjectInserter;
92 import org.eclipse.jgit.lib.ObjectLoader;
93 import org.eclipse.jgit.lib.PersonIdent;
94 import org.eclipse.jgit.lib.ProgressMonitor;
95 import org.eclipse.jgit.lib.Ref;
96 import org.eclipse.jgit.lib.Repository;
97 import org.eclipse.jgit.revwalk.ObjectWalk;
98 import org.eclipse.jgit.revwalk.RevBlob;
99 import org.eclipse.jgit.revwalk.RevCommit;
100 import org.eclipse.jgit.revwalk.RevFlag;
101 import org.eclipse.jgit.revwalk.RevObject;
102 import org.eclipse.jgit.revwalk.RevSort;
103 import org.eclipse.jgit.revwalk.RevTree;
104 import org.eclipse.jgit.revwalk.RevWalk;
105 import org.eclipse.jgit.transport.PacketLineIn.InputOverLimitIOException;
106 import org.eclipse.jgit.transport.ReceiveCommand.Result;
107 import org.eclipse.jgit.util.io.InterruptTimer;
108 import org.eclipse.jgit.util.io.LimitedInputStream;
109 import org.eclipse.jgit.util.io.TimeoutInputStream;
110 import org.eclipse.jgit.util.io.TimeoutOutputStream;
111
112
113
114
115
116
117
118
119 public abstract class BaseReceivePack {
120
121 public static class FirstLine {
122 private final String line;
123 private final Set<String> capabilities;
124
125
126
127
128
129
130
131 public FirstLine(String line) {
132 final HashSet<String> caps = new HashSet<>();
133 final int nul = line.indexOf('\0');
134 if (nul >= 0) {
135 for (String c : line.substring(nul + 1).split(" "))
136 caps.add(c);
137 this.line = line.substring(0, nul);
138 } else
139 this.line = line;
140 this.capabilities = Collections.unmodifiableSet(caps);
141 }
142
143
144 public String getLine() {
145 return line;
146 }
147
148
149 public Set<String> getCapabilities() {
150 return capabilities;
151 }
152 }
153
154
155 private final Repository db;
156
157
158 private final RevWalk walk;
159
160
161
162
163
164
165
166
167
168
169
170
171 private boolean biDirectionalPipe = true;
172
173
174 private boolean expectDataAfterPackFooter;
175
176
177 private ObjectChecker objectChecker;
178
179
180 private boolean allowCreates;
181
182
183 private boolean allowAnyDeletes;
184 private boolean allowBranchDeletes;
185
186
187 private boolean allowNonFastForwards;
188
189
190 private boolean allowPushOptions;
191
192
193
194
195
196 private boolean atomic;
197
198 private boolean allowOfsDelta;
199 private boolean allowQuiet = true;
200
201
202 private PersonIdent refLogIdent;
203
204
205 private AdvertiseRefsHook advertiseRefsHook;
206
207
208 private RefFilter refFilter;
209
210
211 private int timeout;
212
213
214 private InterruptTimer timer;
215
216 private TimeoutInputStream timeoutIn;
217
218
219
220 private OutputStream origOut;
221
222
223 protected InputStream rawIn;
224
225
226 protected OutputStream rawOut;
227
228
229 protected OutputStream msgOut;
230 private SideBandOutputStream errOut;
231
232
233 protected PacketLineIn pckIn;
234
235
236 protected PacketLineOut pckOut;
237
238 private final MessageOutputWrapper msgOutWrapper = new MessageOutputWrapper();
239
240 private PackParser parser;
241
242
243 private Map<String, Ref> refs;
244
245
246 private Set<ObjectId> advertisedHaves;
247
248
249 private Set<String> enabledCapabilities;
250 String userAgent;
251 private Set<ObjectId> clientShallowCommits;
252 private List<ReceiveCommand> commands;
253 private long maxCommandBytes;
254 private long maxDiscardBytes;
255
256 private StringBuilder advertiseError;
257
258
259 private boolean sideBand;
260
261 private boolean quiet;
262
263
264 private PackLock packLock;
265
266 private boolean checkReferencedIsReachable;
267
268
269 private long maxObjectSizeLimit;
270
271
272 private long maxPackSizeLimit = -1;
273
274
275 private Long packSize;
276
277 private PushCertificateParser pushCertificateParser;
278 private SignedPushConfig signedPushConfig;
279 private PushCertificate pushCert;
280 private ReceivedPackStatistics stats;
281
282
283
284
285
286
287
288
289
290
291 public PushCertificate getPushCertificate() {
292 return pushCert;
293 }
294
295
296
297
298
299
300
301
302
303
304
305 public void setPushCertificate(PushCertificate cert) {
306 pushCert = cert;
307 }
308
309
310
311
312
313
314
315 protected BaseReceivePack(Repository into) {
316 db = into;
317 walk = new RevWalk(db);
318
319 TransferConfig tc = db.getConfig().get(TransferConfig.KEY);
320 objectChecker = tc.newReceiveObjectChecker();
321
322 ReceiveConfig rc = db.getConfig().get(ReceiveConfig::new);
323 allowCreates = rc.allowCreates;
324 allowAnyDeletes = true;
325 allowBranchDeletes = rc.allowDeletes;
326 allowNonFastForwards = rc.allowNonFastForwards;
327 allowOfsDelta = rc.allowOfsDelta;
328 allowPushOptions = rc.allowPushOptions;
329 maxCommandBytes = rc.maxCommandBytes;
330 maxDiscardBytes = rc.maxDiscardBytes;
331 advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
332 refFilter = RefFilter.DEFAULT;
333 advertisedHaves = new HashSet<>();
334 clientShallowCommits = new HashSet<>();
335 signedPushConfig = rc.signedPush;
336 }
337
338
339 protected static class ReceiveConfig {
340 final boolean allowCreates;
341 final boolean allowDeletes;
342 final boolean allowNonFastForwards;
343 final boolean allowOfsDelta;
344 final boolean allowPushOptions;
345 final long maxCommandBytes;
346 final long maxDiscardBytes;
347 final SignedPushConfig signedPush;
348
349 ReceiveConfig(Config config) {
350 allowCreates = true;
351 allowDeletes = !config.getBoolean("receive", "denydeletes", false);
352 allowNonFastForwards = !config.getBoolean("receive",
353 "denynonfastforwards", false);
354 allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset",
355 true);
356 allowPushOptions = config.getBoolean("receive", "pushoptions",
357 false);
358 maxCommandBytes = config.getLong("receive",
359 "maxCommandBytes",
360 3 << 20);
361 maxDiscardBytes = config.getLong("receive",
362 "maxCommandDiscardBytes",
363 -1);
364 signedPush = SignedPushConfig.KEY.parse(config);
365 }
366 }
367
368
369
370
371
372
373
374 class MessageOutputWrapper extends OutputStream {
375 @Override
376 public void write(int ch) {
377 if (msgOut != null) {
378 try {
379 msgOut.write(ch);
380 } catch (IOException e) {
381
382 }
383 }
384 }
385
386 @Override
387 public void write(byte[] b, int off, int len) {
388 if (msgOut != null) {
389 try {
390 msgOut.write(b, off, len);
391 } catch (IOException e) {
392
393 }
394 }
395 }
396
397 @Override
398 public void write(byte[] b) {
399 write(b, 0, b.length);
400 }
401
402 @Override
403 public void flush() {
404 if (msgOut != null) {
405 try {
406 msgOut.flush();
407 } catch (IOException e) {
408
409 }
410 }
411 }
412 }
413
414
415
416
417
418
419 protected abstract String getLockMessageProcessName();
420
421
422
423
424
425
426 public final Repository getRepository() {
427 return db;
428 }
429
430
431
432
433
434
435 public final RevWalk getRevWalk() {
436 return walk;
437 }
438
439
440
441
442
443
444
445 public final Map<String, Ref> getAdvertisedRefs() {
446 return refs;
447 }
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466 public void setAdvertisedRefs(Map<String, Ref> allRefs, Set<ObjectId> additionalHaves) {
467 refs = allRefs != null ? allRefs : db.getAllRefs();
468 refs = refFilter.filter(refs);
469 advertisedHaves.clear();
470
471 Ref head = refs.get(Constants.HEAD);
472 if (head != null && head.isSymbolic())
473 refs.remove(Constants.HEAD);
474
475 for (Ref ref : refs.values()) {
476 if (ref.getObjectId() != null)
477 advertisedHaves.add(ref.getObjectId());
478 }
479 if (additionalHaves != null)
480 advertisedHaves.addAll(additionalHaves);
481 else
482 advertisedHaves.addAll(db.getAdditionalHaves());
483 }
484
485
486
487
488
489
490
491
492 public final Set<ObjectId> getAdvertisedObjects() {
493 return advertisedHaves;
494 }
495
496
497
498
499
500
501
502
503
504 public boolean isCheckReferencedObjectsAreReachable() {
505 return checkReferencedIsReachable;
506 }
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529 public void setCheckReferencedObjectsAreReachable(boolean b) {
530 this.checkReferencedIsReachable = b;
531 }
532
533
534
535
536
537
538
539
540 public boolean isBiDirectionalPipe() {
541 return biDirectionalPipe;
542 }
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557 public void setBiDirectionalPipe(boolean twoWay) {
558 biDirectionalPipe = twoWay;
559 }
560
561
562
563
564
565
566 public boolean isExpectDataAfterPackFooter() {
567 return expectDataAfterPackFooter;
568 }
569
570
571
572
573
574
575
576
577 public void setExpectDataAfterPackFooter(boolean e) {
578 expectDataAfterPackFooter = e;
579 }
580
581
582
583
584
585
586
587
588
589 public boolean isCheckReceivedObjects() {
590 return objectChecker != null;
591 }
592
593
594
595
596
597
598
599
600
601 public void setCheckReceivedObjects(boolean check) {
602 if (check && objectChecker == null)
603 setObjectChecker(new ObjectChecker());
604 else if (!check && objectChecker != null)
605 setObjectChecker(null);
606 }
607
608
609
610
611
612
613
614
615
616 public void setObjectChecker(ObjectChecker impl) {
617 objectChecker = impl;
618 }
619
620
621
622
623
624
625 public boolean isAllowCreates() {
626 return allowCreates;
627 }
628
629
630
631
632
633
634
635 public void setAllowCreates(boolean canCreate) {
636 allowCreates = canCreate;
637 }
638
639
640
641
642
643
644 public boolean isAllowDeletes() {
645 return allowAnyDeletes;
646 }
647
648
649
650
651
652
653
654 public void setAllowDeletes(boolean canDelete) {
655 allowAnyDeletes = canDelete;
656 }
657
658
659
660
661
662
663
664 public boolean isAllowBranchDeletes() {
665 return allowBranchDeletes;
666 }
667
668
669
670
671
672
673
674
675
676
677 public void setAllowBranchDeletes(boolean canDelete) {
678 allowBranchDeletes = canDelete;
679 }
680
681
682
683
684
685
686
687
688 public boolean isAllowNonFastForwards() {
689 return allowNonFastForwards;
690 }
691
692
693
694
695
696
697
698
699
700 public void setAllowNonFastForwards(boolean canRewind) {
701 allowNonFastForwards = canRewind;
702 }
703
704
705
706
707
708
709
710
711
712 public boolean isAtomic() {
713 return atomic;
714 }
715
716
717
718
719
720
721
722
723
724
725 public void setAtomic(boolean atomic) {
726 this.atomic = atomic;
727 }
728
729
730
731
732
733
734 public PersonIdent getRefLogIdent() {
735 return refLogIdent;
736 }
737
738
739
740
741
742
743
744
745
746
747
748
749
750 public void setRefLogIdent(PersonIdent pi) {
751 refLogIdent = pi;
752 }
753
754
755
756
757
758
759 public AdvertiseRefsHook getAdvertiseRefsHook() {
760 return advertiseRefsHook;
761 }
762
763
764
765
766
767
768 public RefFilter getRefFilter() {
769 return refFilter;
770 }
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787 public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) {
788 if (advertiseRefsHook != null)
789 this.advertiseRefsHook = advertiseRefsHook;
790 else
791 this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
792 }
793
794
795
796
797
798
799
800
801
802
803
804 public void setRefFilter(RefFilter refFilter) {
805 this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
806 }
807
808
809
810
811
812
813 public int getTimeout() {
814 return timeout;
815 }
816
817
818
819
820
821
822
823
824
825 public void setTimeout(int seconds) {
826 timeout = seconds;
827 }
828
829
830
831
832
833
834
835
836 public void setMaxCommandBytes(long limit) {
837 maxCommandBytes = limit;
838 }
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857 public void setMaxCommandDiscardBytes(long limit) {
858 maxDiscardBytes = limit;
859 }
860
861
862
863
864
865
866
867
868
869
870 public void setMaxObjectSizeLimit(long limit) {
871 maxObjectSizeLimit = limit;
872 }
873
874
875
876
877
878
879
880
881
882
883 public void setMaxPackSizeLimit(long limit) {
884 if (limit < 0)
885 throw new IllegalArgumentException(MessageFormat.format(
886 JGitText.get().receivePackInvalidLimit, Long.valueOf(limit)));
887 maxPackSizeLimit = limit;
888 }
889
890
891
892
893
894
895
896
897
898
899
900
901 public boolean isSideBand() throws RequestNotYetReadException {
902 checkRequestWasRead();
903 return enabledCapabilities.contains(CAPABILITY_SIDE_BAND_64K);
904 }
905
906
907
908
909
910
911
912 public boolean isAllowQuiet() {
913 return allowQuiet;
914 }
915
916
917
918
919
920
921
922
923
924
925
926 public void setAllowQuiet(boolean allow) {
927 allowQuiet = allow;
928 }
929
930
931
932
933
934
935
936 public boolean isAllowPushOptions() {
937 return allowPushOptions;
938 }
939
940
941
942
943
944
945
946
947 public void setAllowPushOptions(boolean allow) {
948 allowPushOptions = allow;
949 }
950
951
952
953
954
955
956
957
958
959
960
961
962 public boolean isQuiet() throws RequestNotYetReadException {
963 checkRequestWasRead();
964 return quiet;
965 }
966
967
968
969
970
971
972
973
974
975
976 public void setSignedPushConfig(SignedPushConfig cfg) {
977 signedPushConfig = cfg;
978 }
979
980 private PushCertificateParser getPushCertificateParser() {
981 if (pushCertificateParser == null) {
982 pushCertificateParser = new PushCertificateParser(db, signedPushConfig);
983 }
984 return pushCertificateParser;
985 }
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002 public String getPeerUserAgent() {
1003 return UserAgent.getAgent(enabledCapabilities, userAgent);
1004 }
1005
1006
1007
1008
1009
1010
1011 public List<ReceiveCommand> getAllCommands() {
1012 return Collections.unmodifiableList(commands);
1013 }
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040 public void sendError(String what) {
1041 if (refs == null) {
1042 if (advertiseError == null)
1043 advertiseError = new StringBuilder();
1044 advertiseError.append(what).append('\n');
1045 } else {
1046 msgOutWrapper.write(Constants.encode("error: " + what + "\n"));
1047 }
1048 }
1049
1050 private void fatalError(String msg) {
1051 if (errOut != null) {
1052 try {
1053 errOut.write(Constants.encode(msg));
1054 errOut.flush();
1055 } catch (IOException e) {
1056
1057 }
1058 } else {
1059 sendError(msg);
1060 }
1061 }
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 public void sendMessage(String what) {
1074 msgOutWrapper.write(Constants.encode(what + "\n"));
1075 }
1076
1077
1078
1079
1080
1081
1082 public OutputStream getMessageOutputStream() {
1083 return msgOutWrapper;
1084 }
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096 public long getPackSize() {
1097 if (packSize != null)
1098 return packSize.longValue();
1099 throw new IllegalStateException(JGitText.get().packSizeNotSetYet);
1100 }
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110 protected Set<ObjectId> getClientShallowCommits() {
1111 return clientShallowCommits;
1112 }
1113
1114
1115
1116
1117
1118
1119 protected boolean hasCommands() {
1120 return !commands.isEmpty();
1121 }
1122
1123
1124
1125
1126
1127
1128 protected boolean hasError() {
1129 return advertiseError != null;
1130 }
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149 protected void init(final InputStream input, final OutputStream output,
1150 final OutputStream messages) {
1151 origOut = output;
1152 rawIn = input;
1153 rawOut = output;
1154 msgOut = messages;
1155
1156 if (timeout > 0) {
1157 final Thread caller = Thread.currentThread();
1158 timer = new InterruptTimer(caller.getName() + "-Timer");
1159 timeoutIn = new TimeoutInputStream(rawIn, timer);
1160 TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer);
1161 timeoutIn.setTimeout(timeout * 1000);
1162 o.setTimeout(timeout * 1000);
1163 rawIn = timeoutIn;
1164 rawOut = o;
1165 }
1166
1167 pckIn = new PacketLineIn(rawIn);
1168 pckOut = new PacketLineOut(rawOut);
1169 pckOut.setFlushOnEnd(false);
1170
1171 enabledCapabilities = new HashSet<>();
1172 commands = new ArrayList<>();
1173 }
1174
1175
1176
1177
1178
1179
1180 protected Map<String, Ref> getAdvertisedOrDefaultRefs() {
1181 if (refs == null)
1182 setAdvertisedRefs(null, null);
1183 return refs;
1184 }
1185
1186
1187
1188
1189
1190
1191
1192 protected void receivePackAndCheckConnectivity() throws IOException {
1193 receivePack();
1194 if (needCheckConnectivity()) {
1195 checkSubmodules();
1196 checkConnectivity();
1197 }
1198 parser = null;
1199 }
1200
1201
1202
1203
1204
1205
1206
1207 protected void unlockPack() throws IOException {
1208 if (packLock != null) {
1209 packLock.unlock();
1210 packLock = null;
1211 }
1212 }
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224 public void sendAdvertisedRefs(RefAdvertiser adv)
1225 throws IOException, ServiceMayNotContinueException {
1226 if (advertiseError != null) {
1227 adv.writeOne("ERR " + advertiseError);
1228 return;
1229 }
1230
1231 try {
1232 advertiseRefsHook.advertiseRefs(this);
1233 } catch (ServiceMayNotContinueException fail) {
1234 if (fail.getMessage() != null) {
1235 adv.writeOne("ERR " + fail.getMessage());
1236 fail.setOutput();
1237 }
1238 throw fail;
1239 }
1240
1241 adv.init(db);
1242 adv.advertiseCapability(CAPABILITY_SIDE_BAND_64K);
1243 adv.advertiseCapability(CAPABILITY_DELETE_REFS);
1244 adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
1245 if (allowQuiet)
1246 adv.advertiseCapability(CAPABILITY_QUIET);
1247 String nonce = getPushCertificateParser().getAdvertiseNonce();
1248 if (nonce != null) {
1249 adv.advertiseCapability(nonce);
1250 }
1251 if (db.getRefDatabase().performsAtomicTransactions())
1252 adv.advertiseCapability(CAPABILITY_ATOMIC);
1253 if (allowOfsDelta)
1254 adv.advertiseCapability(CAPABILITY_OFS_DELTA);
1255 if (allowPushOptions) {
1256 adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS);
1257 }
1258 adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
1259 adv.send(getAdvertisedOrDefaultRefs());
1260 for (ObjectId obj : advertisedHaves)
1261 adv.advertiseHave(obj);
1262 if (adv.isEmpty())
1263 adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
1264 adv.end();
1265 }
1266
1267
1268
1269
1270
1271
1272
1273
1274 @Nullable
1275 public ReceivedPackStatistics getReceivedPackStatistics() {
1276 return stats;
1277 }
1278
1279
1280
1281
1282
1283
1284 protected void recvCommands() throws IOException {
1285 PacketLineIn pck = maxCommandBytes > 0
1286 ? new PacketLineIn(rawIn, maxCommandBytes)
1287 : pckIn;
1288 PushCertificateParser certParser = getPushCertificateParser();
1289 boolean firstPkt = true;
1290 try {
1291 for (;;) {
1292 String line;
1293 try {
1294 line = pck.readString();
1295 } catch (EOFException eof) {
1296 if (commands.isEmpty())
1297 return;
1298 throw eof;
1299 }
1300 if (line == PacketLineIn.END) {
1301 break;
1302 }
1303
1304 if (line.length() >= 48 && line.startsWith("shallow ")) {
1305 parseShallow(line.substring(8, 48));
1306 continue;
1307 }
1308
1309 if (firstPkt) {
1310 firstPkt = false;
1311 FirstLine firstLine = new FirstLine(line);
1312 enabledCapabilities = firstLine.getCapabilities();
1313 line = firstLine.getLine();
1314 enableCapabilities();
1315
1316 if (line.equals(GitProtocolConstants.OPTION_PUSH_CERT)) {
1317 certParser.receiveHeader(pck, !isBiDirectionalPipe());
1318 continue;
1319 }
1320 }
1321
1322 if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) {
1323 certParser.receiveSignature(pck);
1324 continue;
1325 }
1326
1327 ReceiveCommand cmd = parseCommand(line);
1328 if (cmd.getRefName().equals(Constants.HEAD)) {
1329 cmd.setResult(Result.REJECTED_CURRENT_BRANCH);
1330 } else {
1331 cmd.setRef(refs.get(cmd.getRefName()));
1332 }
1333 commands.add(cmd);
1334 if (certParser.enabled()) {
1335 certParser.addCommand(cmd);
1336 }
1337 }
1338 pushCert = certParser.build();
1339 if (hasCommands()) {
1340 readPostCommands(pck);
1341 }
1342 } catch (PackProtocolException e) {
1343 discardCommands();
1344 fatalError(e.getMessage());
1345 throw e;
1346 } catch (InputOverLimitIOException e) {
1347 String msg = JGitText.get().tooManyCommands;
1348 discardCommands();
1349 fatalError(msg);
1350 throw new PackProtocolException(msg);
1351 }
1352 }
1353
1354 private void discardCommands() {
1355 if (sideBand) {
1356 long max = maxDiscardBytes;
1357 if (max < 0) {
1358 max = Math.max(3 * maxCommandBytes, 3L << 20);
1359 }
1360 try {
1361 new PacketLineIn(rawIn, max).discardUntilEnd();
1362 } catch (IOException e) {
1363
1364 }
1365 }
1366 }
1367
1368 private void parseShallow(String idStr) throws PackProtocolException {
1369 ObjectId id;
1370 try {
1371 id = ObjectId.fromString(idStr);
1372 } catch (InvalidObjectIdException e) {
1373 throw new PackProtocolException(e.getMessage(), e);
1374 }
1375 clientShallowCommits.add(id);
1376 }
1377
1378 static ReceiveCommand parseCommand(String line) throws PackProtocolException {
1379 if (line == null || line.length() < 83) {
1380 throw new PackProtocolException(
1381 JGitText.get().errorInvalidProtocolWantedOldNewRef);
1382 }
1383 String oldStr = line.substring(0, 40);
1384 String newStr = line.substring(41, 81);
1385 ObjectId oldId, newId;
1386 try {
1387 oldId = ObjectId.fromString(oldStr);
1388 newId = ObjectId.fromString(newStr);
1389 } catch (InvalidObjectIdException e) {
1390 throw new PackProtocolException(
1391 JGitText.get().errorInvalidProtocolWantedOldNewRef, e);
1392 }
1393 String name = line.substring(82);
1394 if (!Repository.isValidRefName(name)) {
1395 throw new PackProtocolException(
1396 JGitText.get().errorInvalidProtocolWantedOldNewRef);
1397 }
1398 return new ReceiveCommand(oldId, newId, name);
1399 }
1400
1401
1402
1403
1404
1405
1406
1407 void readPostCommands(PacketLineIn in) throws IOException {
1408
1409 }
1410
1411
1412
1413
1414 protected void enableCapabilities() {
1415 sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
1416 quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET);
1417 if (sideBand) {
1418 OutputStream out = rawOut;
1419
1420 rawOut = new SideBandOutputStream(CH_DATA, MAX_BUF, out);
1421 msgOut = new SideBandOutputStream(CH_PROGRESS, MAX_BUF, out);
1422 errOut = new SideBandOutputStream(CH_ERROR, MAX_BUF, out);
1423
1424 pckOut = new PacketLineOut(rawOut);
1425 pckOut.setFlushOnEnd(false);
1426 }
1427 }
1428
1429
1430
1431
1432
1433
1434
1435
1436 protected boolean isCapabilityEnabled(String name) {
1437 return enabledCapabilities.contains(name);
1438 }
1439
1440 void checkRequestWasRead() {
1441 if (enabledCapabilities == null)
1442 throw new RequestNotYetReadException();
1443 }
1444
1445
1446
1447
1448
1449
1450 protected boolean needPack() {
1451 for (ReceiveCommand cmd : commands) {
1452 if (cmd.getType() != ReceiveCommand.Type.DELETE)
1453 return true;
1454 }
1455 return false;
1456 }
1457
1458
1459
1460
1461
1462
1463
1464 private void receivePack() throws IOException {
1465
1466
1467
1468
1469 if (timeoutIn != null)
1470 timeoutIn.setTimeout(10 * timeout * 1000);
1471
1472 ProgressMonitor receiving = NullProgressMonitor.INSTANCE;
1473 ProgressMonitor resolving = NullProgressMonitor.INSTANCE;
1474 if (sideBand && !quiet)
1475 resolving = new SideBandProgressMonitor(msgOut);
1476
1477 try (ObjectInserter ins = db.newObjectInserter()) {
1478 String lockMsg = "jgit receive-pack";
1479 if (getRefLogIdent() != null)
1480 lockMsg += " from " + getRefLogIdent().toExternalString();
1481
1482 parser = ins.newPackParser(packInputStream());
1483 parser.setAllowThin(true);
1484 parser.setNeedNewObjectIds(checkReferencedIsReachable);
1485 parser.setNeedBaseObjectIds(checkReferencedIsReachable);
1486 parser.setCheckEofAfterPackFooter(!biDirectionalPipe
1487 && !isExpectDataAfterPackFooter());
1488 parser.setExpectDataAfterPackFooter(isExpectDataAfterPackFooter());
1489 parser.setObjectChecker(objectChecker);
1490 parser.setLockMessage(lockMsg);
1491 parser.setMaxObjectSizeLimit(maxObjectSizeLimit);
1492 packLock = parser.parse(receiving, resolving);
1493 packSize = Long.valueOf(parser.getPackSize());
1494 stats = parser.getReceivedPackStatistics();
1495 ins.flush();
1496 }
1497
1498 if (timeoutIn != null)
1499 timeoutIn.setTimeout(timeout * 1000);
1500 }
1501
1502 private InputStream packInputStream() {
1503 InputStream packIn = rawIn;
1504 if (maxPackSizeLimit >= 0) {
1505 packIn = new LimitedInputStream(packIn, maxPackSizeLimit) {
1506 @Override
1507 protected void limitExceeded() throws TooLargePackException {
1508 throw new TooLargePackException(limit);
1509 }
1510 };
1511 }
1512 return packIn;
1513 }
1514
1515 private boolean needCheckConnectivity() {
1516 return isCheckReceivedObjects()
1517 || isCheckReferencedObjectsAreReachable()
1518 || !getClientShallowCommits().isEmpty();
1519 }
1520
1521 private void checkSubmodules()
1522 throws IOException {
1523 ObjectDatabase odb = db.getObjectDatabase();
1524 if (objectChecker == null) {
1525 return;
1526 }
1527 for (GitmoduleEntry entry : objectChecker.getGitsubmodules()) {
1528 AnyObjectId blobId = entry.getBlobId();
1529 ObjectLoader blob = odb.open(blobId, Constants.OBJ_BLOB);
1530
1531 SubmoduleValidator.assertValidGitModulesFile(
1532 new String(blob.getBytes(), UTF_8));
1533 }
1534 }
1535
1536 private void checkConnectivity() throws IOException {
1537 ObjectIdSubclassMap<ObjectId> baseObjects = null;
1538 ObjectIdSubclassMap<ObjectId> providedObjects = null;
1539 ProgressMonitor checking = NullProgressMonitor.INSTANCE;
1540 if (sideBand && !quiet) {
1541 SideBandProgressMonitor m = new SideBandProgressMonitor(msgOut);
1542 m.setDelayStart(750, TimeUnit.MILLISECONDS);
1543 checking = m;
1544 }
1545
1546 if (checkReferencedIsReachable) {
1547 baseObjects = parser.getBaseObjectIds();
1548 providedObjects = parser.getNewObjectIds();
1549 }
1550 parser = null;
1551
1552 try (ObjectWalk ow = new ObjectWalk(db)) {
1553 if (baseObjects != null) {
1554 ow.sort(RevSort.TOPO);
1555 if (!baseObjects.isEmpty())
1556 ow.sort(RevSort.BOUNDARY, true);
1557 }
1558
1559 for (ReceiveCommand cmd : commands) {
1560 if (cmd.getResult() != Result.NOT_ATTEMPTED)
1561 continue;
1562 if (cmd.getType() == ReceiveCommand.Type.DELETE)
1563 continue;
1564 ow.markStart(ow.parseAny(cmd.getNewId()));
1565 }
1566 for (ObjectId have : advertisedHaves) {
1567 RevObject o = ow.parseAny(have);
1568 ow.markUninteresting(o);
1569
1570 if (baseObjects != null && !baseObjects.isEmpty()) {
1571 o = ow.peel(o);
1572 if (o instanceof RevCommit)
1573 o = ((RevCommit) o).getTree();
1574 if (o instanceof RevTree)
1575 ow.markUninteresting(o);
1576 }
1577 }
1578
1579 checking.beginTask(JGitText.get().countingObjects,
1580 ProgressMonitor.UNKNOWN);
1581 RevCommit c;
1582 while ((c = ow.next()) != null) {
1583 checking.update(1);
1584 if (providedObjects != null
1585 && !c.has(RevFlag.UNINTERESTING)
1586 && !providedObjects.contains(c))
1587 throw new MissingObjectException(c, Constants.TYPE_COMMIT);
1588 }
1589
1590 RevObject o;
1591 while ((o = ow.nextObject()) != null) {
1592 checking.update(1);
1593 if (o.has(RevFlag.UNINTERESTING))
1594 continue;
1595
1596 if (providedObjects != null) {
1597 if (providedObjects.contains(o))
1598 continue;
1599 else
1600 throw new MissingObjectException(o, o.getType());
1601 }
1602
1603 if (o instanceof RevBlob && !db.hasObject(o))
1604 throw new MissingObjectException(o, Constants.TYPE_BLOB);
1605 }
1606 checking.endTask();
1607
1608 if (baseObjects != null) {
1609 for (ObjectId id : baseObjects) {
1610 o = ow.parseAny(id);
1611 if (!o.has(RevFlag.UNINTERESTING))
1612 throw new MissingObjectException(o, o.getType());
1613 }
1614 }
1615 }
1616 }
1617
1618
1619
1620
1621 protected void validateCommands() {
1622 for (ReceiveCommand cmd : commands) {
1623 final Ref ref = cmd.getRef();
1624 if (cmd.getResult() != Result.NOT_ATTEMPTED)
1625 continue;
1626
1627 if (cmd.getType() == ReceiveCommand.Type.DELETE) {
1628 if (!isAllowDeletes()) {
1629
1630 cmd.setResult(Result.REJECTED_NODELETE);
1631 continue;
1632 }
1633 if (!isAllowBranchDeletes()
1634 && ref.getName().startsWith(Constants.R_HEADS)) {
1635
1636 cmd.setResult(Result.REJECTED_NODELETE);
1637 continue;
1638 }
1639 }
1640
1641 if (cmd.getType() == ReceiveCommand.Type.CREATE) {
1642 if (!isAllowCreates()) {
1643 cmd.setResult(Result.REJECTED_NOCREATE);
1644 continue;
1645 }
1646
1647 if (ref != null && !isAllowNonFastForwards()) {
1648
1649
1650
1651 cmd.setResult(Result.REJECTED_NONFASTFORWARD);
1652 continue;
1653 }
1654
1655 if (ref != null) {
1656
1657
1658
1659 cmd.setResult(Result.REJECTED_OTHER_REASON,
1660 JGitText.get().refAlreadyExists);
1661 continue;
1662 }
1663 }
1664
1665 if (cmd.getType() == ReceiveCommand.Type.DELETE && ref != null) {
1666 ObjectId id = ref.getObjectId();
1667 if (id == null) {
1668 id = ObjectId.zeroId();
1669 }
1670 if (!ObjectId.zeroId().equals(cmd.getOldId())
1671 && !id.equals(cmd.getOldId())) {
1672
1673
1674
1675
1676 cmd.setResult(Result.REJECTED_OTHER_REASON,
1677 JGitText.get().invalidOldIdSent);
1678 continue;
1679 }
1680 }
1681
1682 if (cmd.getType() == ReceiveCommand.Type.UPDATE) {
1683 if (ref == null) {
1684
1685
1686 cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().noSuchRef);
1687 continue;
1688 }
1689 ObjectId id = ref.getObjectId();
1690 if (id == null) {
1691
1692 cmd.setResult(Result.REJECTED_OTHER_REASON,
1693 JGitText.get().cannotUpdateUnbornBranch);
1694 continue;
1695 }
1696
1697 if (!id.equals(cmd.getOldId())) {
1698
1699
1700
1701 cmd.setResult(Result.REJECTED_OTHER_REASON,
1702 JGitText.get().invalidOldIdSent);
1703 continue;
1704 }
1705
1706
1707
1708 RevObject oldObj, newObj;
1709 try {
1710 oldObj = walk.parseAny(cmd.getOldId());
1711 } catch (IOException e) {
1712 cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
1713 .getOldId().name());
1714 continue;
1715 }
1716
1717 try {
1718 newObj = walk.parseAny(cmd.getNewId());
1719 } catch (IOException e) {
1720 cmd.setResult(Result.REJECTED_MISSING_OBJECT, cmd
1721 .getNewId().name());
1722 continue;
1723 }
1724
1725 if (oldObj instanceof RevCommit && newObj instanceof RevCommit) {
1726 try {
1727 if (walk.isMergedInto((RevCommit) oldObj,
1728 (RevCommit) newObj))
1729 cmd.setTypeFastForwardUpdate();
1730 else
1731 cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
1732 } catch (MissingObjectException e) {
1733 cmd.setResult(Result.REJECTED_MISSING_OBJECT, e
1734 .getMessage());
1735 } catch (IOException e) {
1736 cmd.setResult(Result.REJECTED_OTHER_REASON);
1737 }
1738 } else {
1739 cmd.setType(ReceiveCommand.Type.UPDATE_NONFASTFORWARD);
1740 }
1741
1742 if (cmd.getType() == ReceiveCommand.Type.UPDATE_NONFASTFORWARD
1743 && !isAllowNonFastForwards()) {
1744 cmd.setResult(Result.REJECTED_NONFASTFORWARD);
1745 continue;
1746 }
1747 }
1748
1749 if (!cmd.getRefName().startsWith(Constants.R_REFS)
1750 || !Repository.isValidRefName(cmd.getRefName())) {
1751 cmd.setResult(Result.REJECTED_OTHER_REASON, JGitText.get().funnyRefname);
1752 }
1753 }
1754 }
1755
1756
1757
1758
1759
1760
1761
1762 protected boolean anyRejects() {
1763 for (ReceiveCommand cmd : commands) {
1764 if (cmd.getResult() != Result.NOT_ATTEMPTED && cmd.getResult() != Result.OK)
1765 return true;
1766 }
1767 return false;
1768 }
1769
1770
1771
1772
1773
1774
1775 protected void failPendingCommands() {
1776 ReceiveCommand.abort(commands);
1777 }
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787 protected List<ReceiveCommand> filterCommands(Result want) {
1788 return ReceiveCommand.filter(commands, want);
1789 }
1790
1791
1792
1793
1794 protected void executeCommands() {
1795 List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
1796 if (toApply.isEmpty())
1797 return;
1798
1799 ProgressMonitor updating = NullProgressMonitor.INSTANCE;
1800 if (sideBand) {
1801 SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
1802 pm.setDelayStart(250, TimeUnit.MILLISECONDS);
1803 updating = pm;
1804 }
1805
1806 BatchRefUpdate batch = db.getRefDatabase().newBatchUpdate();
1807 batch.setAllowNonFastForwards(isAllowNonFastForwards());
1808 batch.setAtomic(isAtomic());
1809 batch.setRefLogIdent(getRefLogIdent());
1810 batch.setRefLogMessage("push", true);
1811 batch.addCommand(toApply);
1812 try {
1813 batch.setPushCertificate(getPushCertificate());
1814 batch.execute(walk, updating);
1815 } catch (IOException err) {
1816 for (ReceiveCommand cmd : toApply) {
1817 if (cmd.getResult() == Result.NOT_ATTEMPTED)
1818 cmd.reject(err);
1819 }
1820 }
1821 }
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836 protected void sendStatusReport(final boolean forClient,
1837 final Throwable unpackError, final Reporter out) throws IOException {
1838 if (unpackError != null) {
1839 out.sendString("unpack error " + unpackError.getMessage());
1840 if (forClient) {
1841 for (ReceiveCommand cmd : commands) {
1842 out.sendString("ng " + cmd.getRefName()
1843 + " n/a (unpacker error)");
1844 }
1845 }
1846 return;
1847 }
1848
1849 if (forClient)
1850 out.sendString("unpack ok");
1851 for (ReceiveCommand cmd : commands) {
1852 if (cmd.getResult() == Result.OK) {
1853 if (forClient)
1854 out.sendString("ok " + cmd.getRefName());
1855 continue;
1856 }
1857
1858 final StringBuilder r = new StringBuilder();
1859 if (forClient)
1860 r.append("ng ").append(cmd.getRefName()).append(" ");
1861 else
1862 r.append(" ! [rejected] ").append(cmd.getRefName()).append(" (");
1863
1864 switch (cmd.getResult()) {
1865 case NOT_ATTEMPTED:
1866 r.append("server bug; ref not processed");
1867 break;
1868
1869 case REJECTED_NOCREATE:
1870 r.append("creation prohibited");
1871 break;
1872
1873 case REJECTED_NODELETE:
1874 r.append("deletion prohibited");
1875 break;
1876
1877 case REJECTED_NONFASTFORWARD:
1878 r.append("non-fast forward");
1879 break;
1880
1881 case REJECTED_CURRENT_BRANCH:
1882 r.append("branch is currently checked out");
1883 break;
1884
1885 case REJECTED_MISSING_OBJECT:
1886 if (cmd.getMessage() == null)
1887 r.append("missing object(s)");
1888 else if (cmd.getMessage().length() == Constants.OBJECT_ID_STRING_LENGTH) {
1889 r.append("object ");
1890 r.append(cmd.getMessage());
1891 r.append(" missing");
1892 } else
1893 r.append(cmd.getMessage());
1894 break;
1895
1896 case REJECTED_OTHER_REASON:
1897 if (cmd.getMessage() == null)
1898 r.append("unspecified reason");
1899 else
1900 r.append(cmd.getMessage());
1901 break;
1902
1903 case LOCK_FAILURE:
1904 r.append("failed to lock");
1905 break;
1906
1907 case OK:
1908
1909 continue;
1910 }
1911 if (!forClient)
1912 r.append(")");
1913 out.sendString(r.toString());
1914 }
1915 }
1916
1917
1918
1919
1920
1921
1922 protected void close() throws IOException {
1923 if (sideBand) {
1924
1925
1926
1927
1928
1929
1930 ((SideBandOutputStream) msgOut).flushBuffer();
1931 ((SideBandOutputStream) rawOut).flushBuffer();
1932
1933 PacketLineOut plo = new PacketLineOut(origOut);
1934 plo.setFlushOnEnd(false);
1935 plo.end();
1936 }
1937
1938 if (biDirectionalPipe) {
1939
1940
1941
1942
1943 if (!sideBand && msgOut != null)
1944 msgOut.flush();
1945 rawOut.flush();
1946 }
1947 }
1948
1949
1950
1951
1952
1953
1954
1955 protected void release() throws IOException {
1956 walk.close();
1957 unlockPack();
1958 timeoutIn = null;
1959 rawIn = null;
1960 rawOut = null;
1961 msgOut = null;
1962 pckIn = null;
1963 pckOut = null;
1964 refs = null;
1965
1966
1967
1968 commands = null;
1969 if (timer != null) {
1970 try {
1971 timer.terminate();
1972 } finally {
1973 timer = null;
1974 }
1975 }
1976 }
1977
1978
1979 static abstract class Reporter {
1980 abstract void sendString(String s) throws IOException;
1981 }
1982 }