Merge branch 'tb/weak-sha1-for-tail-sum'

The checksum at the tail of files are now computed without
collision detection protection.  This is safe as the consumer of
the information to protect itself from replay attacks checks for
hash collisions independently.

* tb/weak-sha1-for-tail-sum:
  csum-file.c: use unsafe SHA-1 implementation when available
  Makefile: allow specifying a SHA-1 for non-cryptographic uses
  hash.h: scaffolding for _unsafe hashing variants
  sha1: do not redefine `platform_SHA_CTX` and friends
  pack-objects: use finalize_object_file() to rename pack/idx/etc
  finalize_object_file(): implement collision check
  finalize_object_file(): refactor unlink_or_warn() placement
  finalize_object_file(): check for name collision before renaming
This commit is contained in:
Junio C Hamano
2024-10-02 07:46:27 -07:00
11 changed files with 266 additions and 26 deletions

View File

@ -115,6 +115,33 @@ static void git_hash_sha1_final_oid(struct object_id *oid, git_hash_ctx *ctx)
oid->algo = GIT_HASH_SHA1;
}
static void git_hash_sha1_init_unsafe(git_hash_ctx *ctx)
{
git_SHA1_Init_unsafe(&ctx->sha1_unsafe);
}
static void git_hash_sha1_clone_unsafe(git_hash_ctx *dst, const git_hash_ctx *src)
{
git_SHA1_Clone_unsafe(&dst->sha1_unsafe, &src->sha1_unsafe);
}
static void git_hash_sha1_update_unsafe(git_hash_ctx *ctx, const void *data,
size_t len)
{
git_SHA1_Update_unsafe(&ctx->sha1_unsafe, data, len);
}
static void git_hash_sha1_final_unsafe(unsigned char *hash, git_hash_ctx *ctx)
{
git_SHA1_Final_unsafe(hash, &ctx->sha1_unsafe);
}
static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, git_hash_ctx *ctx)
{
git_SHA1_Final_unsafe(oid->hash, &ctx->sha1_unsafe);
memset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);
oid->algo = GIT_HASH_SHA1;
}
static void git_hash_sha256_init(git_hash_ctx *ctx)
{
@ -189,6 +216,11 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
.update_fn = git_hash_unknown_update,
.final_fn = git_hash_unknown_final,
.final_oid_fn = git_hash_unknown_final_oid,
.unsafe_init_fn = git_hash_unknown_init,
.unsafe_clone_fn = git_hash_unknown_clone,
.unsafe_update_fn = git_hash_unknown_update,
.unsafe_final_fn = git_hash_unknown_final,
.unsafe_final_oid_fn = git_hash_unknown_final_oid,
.empty_tree = NULL,
.empty_blob = NULL,
.null_oid = NULL,
@ -204,6 +236,11 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
.update_fn = git_hash_sha1_update,
.final_fn = git_hash_sha1_final,
.final_oid_fn = git_hash_sha1_final_oid,
.unsafe_init_fn = git_hash_sha1_init_unsafe,
.unsafe_clone_fn = git_hash_sha1_clone_unsafe,
.unsafe_update_fn = git_hash_sha1_update_unsafe,
.unsafe_final_fn = git_hash_sha1_final_unsafe,
.unsafe_final_oid_fn = git_hash_sha1_final_oid_unsafe,
.empty_tree = &empty_tree_oid,
.empty_blob = &empty_blob_oid,
.null_oid = &null_oid_sha1,
@ -219,6 +256,11 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
.update_fn = git_hash_sha256_update,
.final_fn = git_hash_sha256_final,
.final_oid_fn = git_hash_sha256_final_oid,
.unsafe_init_fn = git_hash_sha256_init,
.unsafe_clone_fn = git_hash_sha256_clone,
.unsafe_update_fn = git_hash_sha256_update,
.unsafe_final_fn = git_hash_sha256_final,
.unsafe_final_oid_fn = git_hash_sha256_final_oid,
.empty_tree = &empty_tree_oid_sha256,
.empty_blob = &empty_blob_oid_sha256,
.null_oid = &null_oid_sha256,
@ -1932,17 +1974,77 @@ static void write_object_file_prepare_literally(const struct git_hash_algo *algo
hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
}
static int check_collision(const char *filename_a, const char *filename_b)
{
char buf_a[4096], buf_b[4096];
int fd_a = -1, fd_b = -1;
int ret = 0;
fd_a = open(filename_a, O_RDONLY);
if (fd_a < 0) {
ret = error_errno(_("unable to open %s"), filename_a);
goto out;
}
fd_b = open(filename_b, O_RDONLY);
if (fd_b < 0) {
ret = error_errno(_("unable to open %s"), filename_b);
goto out;
}
while (1) {
ssize_t sz_a, sz_b;
sz_a = read_in_full(fd_a, buf_a, sizeof(buf_a));
if (sz_a < 0) {
ret = error_errno(_("unable to read %s"), filename_a);
goto out;
}
sz_b = read_in_full(fd_b, buf_b, sizeof(buf_b));
if (sz_b < 0) {
ret = error_errno(_("unable to read %s"), filename_b);
goto out;
}
if (sz_a != sz_b || memcmp(buf_a, buf_b, sz_a)) {
ret = error(_("files '%s' and '%s' differ in contents"),
filename_a, filename_b);
goto out;
}
if (sz_a < sizeof(buf_a))
break;
}
out:
if (fd_a > -1)
close(fd_a);
if (fd_b > -1)
close(fd_b);
return ret;
}
/*
* Move the just written object into its final resting place.
*/
int finalize_object_file(const char *tmpfile, const char *filename)
{
return finalize_object_file_flags(tmpfile, filename, 0);
}
int finalize_object_file_flags(const char *tmpfile, const char *filename,
enum finalize_object_file_flags flags)
{
struct stat st;
int ret = 0;
if (object_creation_mode == OBJECT_CREATION_USES_RENAMES)
goto try_rename;
else if (link(tmpfile, filename))
ret = errno;
else
unlink_or_warn(tmpfile);
/*
* Coda hack - coda doesn't like cross-directory links,
@ -1957,16 +2059,24 @@ int finalize_object_file(const char *tmpfile, const char *filename)
*/
if (ret && ret != EEXIST) {
try_rename:
if (!rename(tmpfile, filename))
if (!stat(filename, &st))
ret = EEXIST;
else if (!rename(tmpfile, filename))
goto out;
ret = errno;
else
ret = errno;
}
unlink_or_warn(tmpfile);
if (ret) {
if (ret != EEXIST) {
int saved_errno = errno;
unlink_or_warn(tmpfile);
errno = saved_errno;
return error_errno(_("unable to write file %s"), filename);
}
/* FIXME!!! Collision check here ? */
if (!(flags & FOF_SKIP_COLLISION_CHECK) &&
check_collision(tmpfile, filename))
return -1;
unlink_or_warn(tmpfile);
}
out:
@ -2219,7 +2329,8 @@ static int write_loose_object(const struct object_id *oid, char *hdr,
warning_errno(_("failed utime() on %s"), tmp_file.buf);
}
return finalize_object_file(tmp_file.buf, filename.buf);
return finalize_object_file_flags(tmp_file.buf, filename.buf,
FOF_SKIP_COLLISION_CHECK);
}
static int freshen_loose_object(const struct object_id *oid)
@ -2341,7 +2452,8 @@ int stream_loose_object(struct input_stream *in_stream, size_t len,
strbuf_release(&dir);
}
err = finalize_object_file(tmp_file.buf, filename.buf);
err = finalize_object_file_flags(tmp_file.buf, filename.buf,
FOF_SKIP_COLLISION_CHECK);
if (!err && compat)
err = repo_add_loose_object_map(the_repository, oid, &compat_oid);
cleanup: