Commit Diff


commit - 729743d1bc420980f12d403c1b116dc6c7153339
commit + dc671e91ec5fbde514e27ed3d9ce4c6a0459538c
blob - fe49bfbdb846dbdf93b1ea61211e91911f5ee571
blob + 22347c7306d44201b88b5b26b0759e20e8d05c0e
--- libexec/got-fetch-pack/got-fetch-pack.c
+++ libexec/got-fetch-pack/got-fetch-pack.c
@@ -647,7 +647,6 @@ send_fetch_ref(struct imsgbuf *ibuf, struct got_object
 	imsg_close(ibuf, wbuf);
 	return got_privsep_flush_imsg(ibuf);
 }
-
 
 static const struct got_error *
 fetch_pack(int fd, int packfd, struct got_object_id *packid,
@@ -670,8 +669,14 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 	struct got_pathlist_entry *pe;
 	int sent_my_capabilites = 0, have_sidebands = 0;
 	int found_branch = 0;
+	SHA1_CTX sha1_ctx;
+	uint8_t pack_sha1[SHA1_DIGEST_LENGTH];
+	uint8_t sha1_buf[SHA1_DIGEST_LENGTH];
+	size_t sha1_buf_len = 0;
+	ssize_t w;
 
 	TAILQ_INIT(&symrefs);
+	SHA1Init(&sha1_ctx);
 
 	have = malloc(refsz * sizeof(have[0]));
 	if (have == NULL)
@@ -925,7 +930,7 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 		have_sidebands = 1;
 
 	while (1) {
-		ssize_t r = 0, w;
+		ssize_t r = 0;
 		int datalen = -1;
 
 		if (have_sidebands) {
@@ -999,7 +1004,59 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 			if (r <= 0)
 				break;
 		}
+
+		/*
+		 * An expected SHA1 checksum sits at the end of the pack file.
+		 * Since we don't know the file size ahead of time we have to
+		 * keep SHA1_DIGEST_LENGTH bytes buffered and avoid mixing
+		 * those bytes into our SHA1 checksum computation until we
+		 * know for sure that additional pack file data bytes follow.
+		 *
+		 * We can assume r > 0 since otherwise the loop would exit.
+		 */
+		if (r < SHA1_DIGEST_LENGTH) {
+			if (sha1_buf_len < SHA1_DIGEST_LENGTH) {
+				/*
+				 * If there's enough buffered + read data to
+				 * fill up the buffer then shift a sufficient
+				 * amount of bytes out at the front to make
+				 * room, mixing those bytes into the checksum.
+				 */
+				while (sha1_buf_len > 0 &&
+				    sha1_buf_len + r > SHA1_DIGEST_LENGTH) {
+					SHA1Update(&sha1_ctx, sha1_buf, 1);
+					memmove(sha1_buf, sha1_buf + 1, 1);
+					sha1_buf_len--;
+				}
+
+				/* Buffer potential checksum bytes. */
+				memcpy(sha1_buf + sha1_buf_len, buf, r);
+				sha1_buf_len += r;
+			} else {
+				/*
+				 * Mix in previously buffered bytes which
+				 * are not part of the checksum after all.
+				 */
+				SHA1Update(&sha1_ctx, sha1_buf, r);
 
+				/* Update potential checksum buffer. */
+				memmove(sha1_buf, sha1_buf + r,
+				    sha1_buf_len - r);
+				memcpy(sha1_buf + sha1_buf_len - r, buf, r);
+			}
+		} else {
+			/* Mix in any previously buffered bytes. */
+			SHA1Update(&sha1_ctx, sha1_buf, sha1_buf_len);
+
+			/* Mix in bytes read minus potential checksum bytes. */
+			SHA1Update(&sha1_ctx, buf, r - SHA1_DIGEST_LENGTH);
+
+			/* Buffer potential checksum bytes. */
+			memcpy(sha1_buf, buf + r - SHA1_DIGEST_LENGTH,
+			    SHA1_DIGEST_LENGTH);
+			sha1_buf_len = SHA1_DIGEST_LENGTH;
+		}
+	
 		/* Write packfile data to temporary pack file. */
 		w = write(packfd, buf, r);
 		if (w == -1) {
@@ -1023,6 +1080,13 @@ fetch_pack(int fd, int packfd, struct got_object_id *p
 	err = send_fetch_download_progress(ibuf, packsz);
 	if (err)
 		goto done;
+
+	SHA1Final(pack_sha1, &sha1_ctx);
+	if (sha1_buf_len != SHA1_DIGEST_LENGTH ||
+	    memcmp(pack_sha1, sha1_buf, sha1_buf_len) != 0) {
+		err = got_error_msg(GOT_ERR_BAD_PACKFILE,
+		    "pack file checksum mismatch");
+	}
 done:
 	TAILQ_FOREACH(pe, &symrefs, entry) {
 		free((void *)pe->path);