Exemple #1
0
  private static byte[] getDelta(ObjectReader reader, RevObject obj)
      throws IOException, MissingObjectException, StoredObjectRepresentationNotAvailableException {
    ObjectReuseAsIs asis = (ObjectReuseAsIs) reader;
    ObjectToPack target = asis.newObjectToPack(obj, obj.getType());

    PackWriter pw =
        new PackWriter(reader) {
          @Override
          public void select(ObjectToPack otp, StoredObjectRepresentation next) {
            otp.select(next);
          }
        };

    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    asis.selectObjectRepresentation(
        pw, NullProgressMonitor.INSTANCE, Collections.singleton(target));
    asis.copyObjectAsIs(new PackOutputStream(NullProgressMonitor.INSTANCE, buf, pw), target, true);

    // At this point the object header has no delta information,
    // because it was output as though it were a whole object.
    // Skip over the header and inflate.
    //
    byte[] bufArray = buf.toByteArray();
    int ptr = 0;
    while ((bufArray[ptr] & 0x80) != 0) ptr++;
    ptr++;

    @SuppressWarnings("resource" /* java 7 */)
    TemporaryBuffer.Heap raw = new TemporaryBuffer.Heap(bufArray.length);
    InflaterInputStream inf =
        new InflaterInputStream(new ByteArrayInputStream(bufArray, ptr, bufArray.length));
    raw.copy(inf);
    inf.close();
    return raw.toByteArray();
  }
  private static void packHeader(TemporaryBuffer.Heap tinyPack, int cnt) throws IOException {
    final byte[] hdr = new byte[8];
    NB.encodeInt32(hdr, 0, 2);
    NB.encodeInt32(hdr, 4, cnt);

    tinyPack.write(Constants.PACK_SIGNATURE);
    tinyPack.write(hdr, 0, 8);
  }
  @Test
  public void testUsingHiddenDeltaBaseFails() throws Exception {
    byte[] delta = {0x1, 0x1, 0x1, 'c'};
    TestRepository<Repository> s = new TestRepository<Repository>(src);
    RevCommit N =
        s.commit()
            .parent(B)
            .add("q", s.blob(BinaryDelta.apply(dst.open(b).getCachedBytes(), delta)))
            .create();

    final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
    packHeader(pack, 3);
    copy(pack, src.open(N));
    copy(pack, src.open(s.parseBody(N).getTree()));
    pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
    b.copyRawTo(pack);
    deflate(pack, delta);
    digest(pack);

    final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
    final PacketLineOut inPckLine = new PacketLineOut(inBuf);
    inPckLine.writeString(
        ObjectId.zeroId().name()
            + ' '
            + N.name()
            + ' '
            + "refs/heads/s"
            + '\0'
            + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
    inPckLine.end();
    pack.writeTo(inBuf, PM);

    final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
    final ReceivePack rp = new ReceivePack(dst);
    rp.setCheckReceivedObjects(true);
    rp.setCheckReferencedObjectsAreReachable(true);
    rp.setAdvertiseRefsHook(new HidePrivateHook());
    try {
      receive(rp, inBuf, outBuf);
      fail("Expected UnpackException");
    } catch (UnpackException failed) {
      Throwable err = failed.getCause();
      assertTrue(err instanceof MissingObjectException);
      MissingObjectException moe = (MissingObjectException) err;
      assertEquals(b, moe.getObjectId());
    }

    final PacketLineIn r = asPacketLineIn(outBuf);
    String master = r.readString();
    int nul = master.indexOf('\0');
    assertTrue("has capability list", nul > 0);
    assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
    assertSame(PacketLineIn.END, r.readString());

    assertEquals("unpack error Missing blob " + b.name(), r.readString());
    assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString());
    assertSame(PacketLineIn.END, r.readString());
  }
  @Test
  public void testUsingUnknownBlobFails() throws Exception {
    // Try to use the 'n' blob that is not on the server.
    //
    TestRepository<Repository> s = new TestRepository<Repository>(src);
    RevBlob n = s.blob("n");
    RevCommit N = s.commit().parent(B).add("q", n).create();

    // But don't include it in the pack.
    //
    final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);
    packHeader(pack, 2);
    copy(pack, src.open(N));
    copy(pack, src.open(s.parseBody(N).getTree()));
    digest(pack);

    final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(1024);
    final PacketLineOut inPckLine = new PacketLineOut(inBuf);
    inPckLine.writeString(
        ObjectId.zeroId().name()
            + ' '
            + N.name()
            + ' '
            + "refs/heads/s"
            + '\0'
            + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
    inPckLine.end();
    pack.writeTo(inBuf, PM);

    final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
    final ReceivePack rp = new ReceivePack(dst);
    rp.setCheckReceivedObjects(true);
    rp.setCheckReferencedObjectsAreReachable(true);
    rp.setAdvertiseRefsHook(new HidePrivateHook());
    try {
      receive(rp, inBuf, outBuf);
      fail("Expected UnpackException");
    } catch (UnpackException failed) {
      Throwable err = failed.getCause();
      assertTrue(err instanceof MissingObjectException);
      MissingObjectException moe = (MissingObjectException) err;
      assertEquals(n, moe.getObjectId());
    }

    final PacketLineIn r = asPacketLineIn(outBuf);
    String master = r.readString();
    int nul = master.indexOf('\0');
    assertTrue("has capability list", nul > 0);
    assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
    assertSame(PacketLineIn.END, r.readString());

    assertEquals("unpack error Missing blob " + n.name(), r.readString());
    assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString());
    assertSame(PacketLineIn.END, r.readString());
  }
  private void openPack(TemporaryBuffer.Heap buf) throws IOException {
    if (inserter == null) inserter = src.newObjectInserter();

    final byte[] raw = buf.toByteArray();
    PackParser p = inserter.newPackParser(new ByteArrayInputStream(raw));
    p.setAllowThin(true);
    p.parse(PM);
  }
  @Test
  public void testCreateBranchAtHiddenCommitFails() throws Exception {
    final TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(64);
    packHeader(pack, 0);
    digest(pack);

    final TemporaryBuffer.Heap inBuf = new TemporaryBuffer.Heap(256);
    final PacketLineOut inPckLine = new PacketLineOut(inBuf);
    inPckLine.writeString(
        ObjectId.zeroId().name()
            + ' '
            + P.name()
            + ' '
            + "refs/heads/s"
            + '\0'
            + BasePackPushConnection.CAPABILITY_REPORT_STATUS);
    inPckLine.end();
    pack.writeTo(inBuf, PM);

    final TemporaryBuffer.Heap outBuf = new TemporaryBuffer.Heap(1024);
    final ReceivePack rp = new ReceivePack(dst);
    rp.setCheckReceivedObjects(true);
    rp.setCheckReferencedObjectsAreReachable(true);
    rp.setAdvertiseRefsHook(new HidePrivateHook());
    try {
      receive(rp, inBuf, outBuf);
      fail("Expected UnpackException");
    } catch (UnpackException failed) {
      Throwable err = failed.getCause();
      assertTrue(err instanceof MissingObjectException);
      MissingObjectException moe = (MissingObjectException) err;
      assertEquals(P, moe.getObjectId());
    }

    final PacketLineIn r = asPacketLineIn(outBuf);
    String master = r.readString();
    int nul = master.indexOf('\0');
    assertTrue("has capability list", nul > 0);
    assertEquals(B.name() + ' ' + R_MASTER, master.substring(0, nul));
    assertSame(PacketLineIn.END, r.readString());

    assertEquals("unpack error Missing commit " + P.name(), r.readString());
    assertEquals("ng refs/heads/s n/a (unpacker error)", r.readString());
    assertSame(PacketLineIn.END, r.readString());
  }
 private static void deflate(TemporaryBuffer.Heap tinyPack, final byte[] content)
     throws IOException {
   final Deflater deflater = new Deflater();
   final byte[] buf = new byte[128];
   deflater.setInput(content, 0, content.length);
   deflater.finish();
   do {
     final int n = deflater.deflate(buf, 0, buf.length);
     if (n > 0) tinyPack.write(buf, 0, n);
   } while (!deflater.finished());
 }
 private static void copy(TemporaryBuffer.Heap tinyPack, ObjectLoader ldr) throws IOException {
   final byte[] buf = new byte[64];
   final byte[] content = ldr.getCachedBytes();
   int dataLength = content.length;
   int nextLength = dataLength >>> 4;
   int size = 0;
   buf[size++] =
       (byte) ((nextLength > 0 ? 0x80 : 0x00) | (ldr.getType() << 4) | (dataLength & 0x0F));
   dataLength = nextLength;
   while (dataLength > 0) {
     nextLength >>>= 7;
     buf[size++] = (byte) ((nextLength > 0 ? 0x80 : 0x00) | (dataLength & 0x7F));
     dataLength = nextLength;
   }
   tinyPack.write(buf, 0, size);
   deflate(tinyPack, content);
 }
  private void negotiate(final ProgressMonitor monitor) throws IOException, CancelledException {
    final MutableObjectId ackId = new MutableObjectId();
    int resultsPending = 0;
    int havesSent = 0;
    int havesSinceLastContinue = 0;
    boolean receivedContinue = false;
    boolean receivedAck = false;

    if (statelessRPC) state.writeTo(out, null);

    negotiateBegin();
    SEND_HAVES:
    for (; ; ) {
      final RevCommit c = walk.next();
      if (c == null) break SEND_HAVES;

      pckOut.writeString("have " + c.getId().name() + "\n");
      havesSent++;
      havesSinceLastContinue++;

      if ((31 & havesSent) != 0) {
        // We group the have lines into blocks of 32, each marked
        // with a flush (aka end). This one is within a block so
        // continue with another have line.
        //
        continue;
      }

      if (monitor.isCancelled()) throw new CancelledException();

      pckOut.end();
      resultsPending++; // Each end will cause a result to come back.

      if (havesSent == 32 && !statelessRPC) {
        // On the first block we race ahead and try to send
        // more of the second block while waiting for the
        // remote to respond to our first block request.
        // This keeps us one block ahead of the peer.
        //
        continue;
      }

      READ_RESULT:
      for (; ; ) {
        final AckNackResult anr = pckIn.readACK(ackId);
        switch (anr) {
          case NAK:
            // More have lines are necessary to compute the
            // pack on the remote side. Keep doing that.
            //
            resultsPending--;
            break READ_RESULT;

          case ACK:
            // The remote side is happy and knows exactly what
            // to send us. There is no further negotiation and
            // we can break out immediately.
            //
            multiAck = MultiAck.OFF;
            resultsPending = 0;
            receivedAck = true;
            if (statelessRPC) state.writeTo(out, null);
            break SEND_HAVES;

          case ACK_CONTINUE:
          case ACK_COMMON:
          case ACK_READY:
            // The server knows this commit (ackId). We don't
            // need to send any further along its ancestry, but
            // we need to continue to talk about other parts of
            // our local history.
            //
            markCommon(walk.parseAny(ackId), anr);
            receivedAck = true;
            receivedContinue = true;
            havesSinceLastContinue = 0;
            break;
        }

        if (monitor.isCancelled()) throw new CancelledException();
      }

      if (statelessRPC) state.writeTo(out, null);

      if (receivedContinue && havesSinceLastContinue > MAX_HAVES) {
        // Our history must be really different from the remote's.
        // We just sent a whole slew of have lines, and it did not
        // recognize any of them. Avoid sending our entire history
        // to them by giving up early.
        //
        break SEND_HAVES;
      }
    }

    // Tell the remote side we have run out of things to talk about.
    //
    if (monitor.isCancelled()) throw new CancelledException();

    // When statelessRPC is true we should always leave SEND_HAVES
    // loop above while in the middle of a request. This allows us
    // to just write done immediately.
    //
    pckOut.writeString("done\n");
    pckOut.flush();

    if (!receivedAck) {
      // Apparently if we have never received an ACK earlier
      // there is one more result expected from the done we
      // just sent to the remote.
      //
      multiAck = MultiAck.OFF;
      resultsPending++;
    }

    READ_RESULT:
    while (resultsPending > 0 || multiAck != MultiAck.OFF) {
      final AckNackResult anr = pckIn.readACK(ackId);
      resultsPending--;
      switch (anr) {
        case NAK:
          // A NAK is a response to an end we queued earlier
          // we eat it and look for another ACK/NAK message.
          //
          break;

        case ACK:
          // A solitary ACK at this point means the remote won't
          // speak anymore, but is going to send us a pack now.
          //
          break READ_RESULT;

        case ACK_CONTINUE:
        case ACK_COMMON:
        case ACK_READY:
          // We will expect a normal ACK to break out of the loop.
          //
          multiAck = MultiAck.CONTINUE;
          break;
      }

      if (monitor.isCancelled()) throw new CancelledException();
    }
  }
 private static PacketLineIn asPacketLineIn(TemporaryBuffer.Heap buf) throws IOException {
   return new PacketLineIn(new ByteArrayInputStream(buf.toByteArray()));
 }
 private static void digest(TemporaryBuffer.Heap buf) throws IOException {
   MessageDigest md = Constants.newMessageDigest();
   md.update(buf.toByteArray());
   buf.write(md.digest());
 }
 private static void receive(
     final ReceivePack rp, final TemporaryBuffer.Heap inBuf, final TemporaryBuffer.Heap outBuf)
     throws IOException {
   rp.receive(new ByteArrayInputStream(inBuf.toByteArray()), outBuf, null);
 }
  @Test
  public void testSuccess() throws Exception {
    // Manually force a delta of an object so we reuse it later.
    //
    TemporaryBuffer.Heap pack = new TemporaryBuffer.Heap(1024);

    packHeader(pack, 2);
    pack.write((Constants.OBJ_BLOB) << 4 | 1);
    deflate(pack, new byte[] {'a'});

    pack.write((Constants.OBJ_REF_DELTA) << 4 | 4);
    a.copyRawTo(pack);
    deflate(pack, new byte[] {0x1, 0x1, 0x1, 'b'});

    digest(pack);
    openPack(pack);

    // Verify the only storage of b is our packed delta above.
    //
    ObjectDirectory od = (ObjectDirectory) src.getObjectDatabase();
    assertTrue("has b", src.hasObject(b));
    assertFalse("b not loose", od.fileFor(b).exists());

    // Now use b but in a different commit than what is hidden.
    //
    TestRepository<Repository> s = new TestRepository<Repository>(src);
    RevCommit N = s.commit().parent(B).add("q", b).create();
    s.update(R_MASTER, N);

    // Push this new content to the remote, doing strict validation.
    //
    TransportLocal t =
        new TransportLocal(src, uriOf(dst), dst.getDirectory()) {
          @Override
          ReceivePack createReceivePack(final Repository db) {
            db.close();
            dst.incrementOpen();

            final ReceivePack rp = super.createReceivePack(dst);
            rp.setCheckReceivedObjects(true);
            rp.setCheckReferencedObjectsAreReachable(true);
            rp.setAdvertiseRefsHook(new HidePrivateHook());
            return rp;
          }
        };
    RemoteRefUpdate u =
        new RemoteRefUpdate( //
            src, //
            R_MASTER, // src name
            R_MASTER, // dst name
            false, // do not force update
            null, // local tracking branch
            null // expected id
            );
    PushResult r;
    try {
      t.setPushThin(true);
      r = t.push(PM, Collections.singleton(u));
    } finally {
      t.close();
    }

    assertNotNull("have result", r);
    assertNull("private not advertised", r.getAdvertisedRef(R_PRIVATE));
    assertSame("master updated", RemoteRefUpdate.Status.OK, u.getStatus());
    assertEquals(N, dst.resolve(R_MASTER));
  }