commit 393fb88d6194f734060bc169da1b73f70648a3ab from: Stefan Sperling date: Sat Mar 21 17:28:29 2020 UTC handle reference-only updates in 'got fetch' where server sends an empty pack commit - 688f11b313a1cd629b736df1a076e0c24f81c4f7 commit + 393fb88d6194f734060bc169da1b73f70648a3ab blob - 1cdd3ca72ccdaa921a1b2f3222aca79c7e483b50 blob + 30d3009ca35c6738728ca92a15e5b4854bfd053f --- lib/fetch.c +++ lib/fetch.c @@ -410,6 +410,8 @@ got_fetch_pack(struct got_object_id **pack_hash, struc struct got_reflist_head my_refs; struct got_reflist_entry *re; off_t packfile_size = 0; + struct got_packfile_hdr pack_hdr; + uint32_t nobj = 0; char *ref_prefix = NULL; size_t ref_prefixlen = 0; char *path; @@ -519,11 +521,6 @@ got_fetch_pack(struct got_object_id **pack_hash, struc if (err) goto done; } - npackfd = dup(packfd); - if (npackfd == -1) { - err = got_error_from_errno("dup"); - goto done; - } if (list_refs_only) { idxfd = got_opentempfd(); if (idxfd == -1) { @@ -584,14 +581,15 @@ got_fetch_pack(struct got_object_id **pack_hash, struc if (err != NULL) goto done; nfetchfd = -1; - err = got_privsep_send_fetch_outfd(&fetchibuf, npackfd); - if (err != NULL) - goto done; npackfd = dup(packfd); if (npackfd == -1) { err = got_error_from_errno("dup"); goto done; } + err = got_privsep_send_fetch_outfd(&fetchibuf, npackfd); + if (err != NULL) + goto done; + npackfd = -1; packfile_size = 0; progress = calloc(GOT_FETCH_PKTMAX, 1); @@ -668,20 +666,73 @@ got_fetch_pack(struct got_object_id **pack_hash, struc } if (waitpid(fetchpid, &fetchstatus, 0) == -1) { err = got_error_from_errno("waitpid"); + goto done; + } + + if (lseek(packfd, 0, SEEK_SET) == -1) { + err = got_error_from_errno("lseek"); goto done; } /* If zero data was fetched without error we are already up-to-date. */ if (packfile_size == 0) + goto done; + else if (packfile_size < sizeof(pack_hdr) + SHA1_DIGEST_LENGTH) { + err = got_error_msg(GOT_ERR_BAD_PACKFILE, "short pack file"); + goto done; + } else { + ssize_t n; + + n = read(packfd, &pack_hdr, sizeof(pack_hdr)); + if (n == -1) { + err = got_error_from_errno("read"); + goto done; + } + if (n != sizeof(pack_hdr)) { + err = got_error(GOT_ERR_IO); + goto done; + } + if (pack_hdr.signature != htobe32(GOT_PACKFILE_SIGNATURE)) { + err = got_error_msg(GOT_ERR_BAD_PACKFILE, + "bad pack file signature"); + goto done; + } + if (pack_hdr.version != htobe32(GOT_PACKFILE_VERSION)) { + err = got_error_msg(GOT_ERR_BAD_PACKFILE, + "bad pack file version"); + goto done; + } + nobj = betoh32(pack_hdr.nobjects); + if (nobj == 0 && + packfile_size > sizeof(pack_hdr) + SHA1_DIGEST_LENGTH) + return got_error_msg(GOT_ERR_BAD_PACKFILE, + "bad pack file with zero objects"); + if (nobj != 0 && + packfile_size <= sizeof(pack_hdr) + SHA1_DIGEST_LENGTH) + return got_error_msg(GOT_ERR_BAD_PACKFILE, + "empty pack file with non-zero object count"); + } + + /* + * If the pack file contains no objects, we may only need to update + * references in our repository. The caller will take care of that. + */ + if (nobj == 0) goto done; if (lseek(packfd, 0, SEEK_SET) == -1) { err = got_error_from_errno("lseek"); goto done; } + err = check_pack_hash(packfd, packfile_size, (*pack_hash)->sha1); if (err) goto done; + + if (lseek(packfd, 0, SEEK_SET) == -1) { + err = got_error_from_errno("lseek"); + goto done; + } if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, imsg_idxfds) == -1) { err = got_error_from_errno("socketpair"); @@ -700,6 +751,11 @@ got_fetch_pack(struct got_object_id **pack_hash, struc } imsg_init(&idxibuf, imsg_idxfds[0]); + npackfd = dup(packfd); + if (npackfd == -1) { + err = got_error_from_errno("dup"); + goto done; + } err = got_privsep_send_index_pack_req(&idxibuf, (*pack_hash)->sha1, npackfd); if (err != NULL)