aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile25
-rw-r--r--block-sha1/sha1.h2
-rw-r--r--csum-file.c18
-rw-r--r--hash.h72
-rw-r--r--object-file.c124
-rw-r--r--object-file.h6
-rw-r--r--pack-write.c7
-rw-r--r--sha1/openssl.h2
-rw-r--r--sha1dc_git.h3
-rwxr-xr-xt/t5303-pack-corruption-resilience.sh7
-rw-r--r--tmp-objdir.c26
11 files changed, 266 insertions, 26 deletions
diff --git a/Makefile b/Makefile
index 7344a7f725..2dde1fd2b8 100644
--- a/Makefile
+++ b/Makefile
@@ -521,6 +521,10 @@ include shared.mak
# Define APPLE_COMMON_CRYPTO_SHA1 to use Apple's CommonCrypto for
# SHA-1.
#
+# Define the same Makefile knobs as above, but suffixed with _UNSAFE to
+# use the corresponding implementations for unsafe SHA-1 hashing for
+# non-cryptographic purposes.
+#
# If don't enable any of the *_SHA1 settings in this section, Git will
# default to its built-in sha1collisiondetection library, which is a
# collision-detecting sha1 This is slower, but may detect attempted
@@ -1997,6 +2001,27 @@ endif
endif
endif
+ifdef OPENSSL_SHA1_UNSAFE
+ifndef OPENSSL_SHA1
+ EXTLIBS += $(LIB_4_CRYPTO)
+ BASIC_CFLAGS += -DSHA1_OPENSSL_UNSAFE
+endif
+else
+ifdef BLK_SHA1_UNSAFE
+ifndef BLK_SHA1
+ LIB_OBJS += block-sha1/sha1.o
+ BASIC_CFLAGS += -DSHA1_BLK_UNSAFE
+endif
+else
+ifdef APPLE_COMMON_CRYPTO_SHA1_UNSAFE
+ifndef APPLE_COMMON_CRYPTO_SHA1
+ COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
+ BASIC_CFLAGS += -DSHA1_APPLE_UNSAFE
+endif
+endif
+endif
+endif
+
ifdef OPENSSL_SHA256
EXTLIBS += $(LIB_4_CRYPTO)
BASIC_CFLAGS += -DSHA256_OPENSSL
diff --git a/block-sha1/sha1.h b/block-sha1/sha1.h
index 9fb0441b98..47bb916636 100644
--- a/block-sha1/sha1.h
+++ b/block-sha1/sha1.h
@@ -16,7 +16,9 @@ void blk_SHA1_Init(blk_SHA_CTX *ctx);
void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, size_t len);
void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
+#ifndef platform_SHA_CTX
#define platform_SHA_CTX blk_SHA_CTX
#define platform_SHA1_Init blk_SHA1_Init
#define platform_SHA1_Update blk_SHA1_Update
#define platform_SHA1_Final blk_SHA1_Final
+#endif
diff --git a/csum-file.c b/csum-file.c
index bf82ad8f9f..c203ebf11b 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -50,7 +50,7 @@ void hashflush(struct hashfile *f)
if (offset) {
if (!f->skip_hash)
- the_hash_algo->update_fn(&f->ctx, f->buffer, offset);
+ the_hash_algo->unsafe_update_fn(&f->ctx, f->buffer, offset);
flush(f, f->buffer, offset);
f->offset = 0;
}
@@ -73,7 +73,7 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
if (f->skip_hash)
hashclr(f->buffer, the_repository->hash_algo);
else
- the_hash_algo->final_fn(f->buffer, &f->ctx);
+ the_hash_algo->unsafe_final_fn(f->buffer, &f->ctx);
if (result)
hashcpy(result, f->buffer, the_repository->hash_algo);
@@ -128,7 +128,7 @@ void hashwrite(struct hashfile *f, const void *buf, unsigned int count)
* f->offset is necessarily zero.
*/
if (!f->skip_hash)
- the_hash_algo->update_fn(&f->ctx, buf, nr);
+ the_hash_algo->unsafe_update_fn(&f->ctx, buf, nr);
flush(f, buf, nr);
} else {
/*
@@ -174,7 +174,7 @@ static struct hashfile *hashfd_internal(int fd, const char *name,
f->name = name;
f->do_crc = 0;
f->skip_hash = 0;
- the_hash_algo->init_fn(&f->ctx);
+ the_hash_algo->unsafe_init_fn(&f->ctx);
f->buffer_len = buffer_len;
f->buffer = xmalloc(buffer_len);
@@ -208,7 +208,7 @@ void hashfile_checkpoint(struct hashfile *f, struct hashfile_checkpoint *checkpo
{
hashflush(f);
checkpoint->offset = f->total;
- the_hash_algo->clone_fn(&checkpoint->ctx, &f->ctx);
+ the_hash_algo->unsafe_clone_fn(&checkpoint->ctx, &f->ctx);
}
int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint)
@@ -219,7 +219,7 @@ int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint
lseek(f->fd, offset, SEEK_SET) != offset)
return -1;
f->total = offset;
- the_hash_algo->clone_fn(&f->ctx, &checkpoint->ctx);
+ the_hash_algo->unsafe_clone_fn(&f->ctx, &checkpoint->ctx);
f->offset = 0; /* hashflush() was called in checkpoint */
return 0;
}
@@ -245,9 +245,9 @@ int hashfile_checksum_valid(const unsigned char *data, size_t total_len)
if (total_len < the_hash_algo->rawsz)
return 0; /* say "too short"? */
- the_hash_algo->init_fn(&ctx);
- the_hash_algo->update_fn(&ctx, data, data_len);
- the_hash_algo->final_fn(got, &ctx);
+ the_hash_algo->unsafe_init_fn(&ctx);
+ the_hash_algo->unsafe_update_fn(&ctx, data, data_len);
+ the_hash_algo->unsafe_final_fn(got, &ctx);
return hasheq(got, data + data_len, the_repository->hash_algo);
}
diff --git a/hash.h b/hash.h
index 72ffbc862e..f97f858307 100644
--- a/hash.h
+++ b/hash.h
@@ -15,6 +15,36 @@
#include "block-sha1/sha1.h"
#endif
+#if defined(SHA1_APPLE_UNSAFE)
+# include <CommonCrypto/CommonDigest.h>
+# define platform_SHA_CTX_unsafe CC_SHA1_CTX
+# define platform_SHA1_Init_unsafe CC_SHA1_Init
+# define platform_SHA1_Update_unsafe CC_SHA1_Update
+# define platform_SHA1_Final_unsafe CC_SHA1_Final
+#elif defined(SHA1_OPENSSL_UNSAFE)
+# include <openssl/sha.h>
+# if defined(OPENSSL_API_LEVEL) && OPENSSL_API_LEVEL >= 3
+# define SHA1_NEEDS_CLONE_HELPER_UNSAFE
+# include "sha1/openssl.h"
+# define platform_SHA_CTX_unsafe openssl_SHA1_CTX
+# define platform_SHA1_Init_unsafe openssl_SHA1_Init
+# define platform_SHA1_Clone_unsafe openssl_SHA1_Clone
+# define platform_SHA1_Update_unsafe openssl_SHA1_Update
+# define platform_SHA1_Final_unsafe openssl_SHA1_Final
+# else
+# define platform_SHA_CTX_unsafe SHA_CTX
+# define platform_SHA1_Init_unsafe SHA1_Init
+# define platform_SHA1_Update_unsafe SHA1_Update
+# define platform_SHA1_Final_unsafe SHA1_Final
+# endif
+#elif defined(SHA1_BLK_UNSAFE)
+# include "block-sha1/sha1.h"
+# define platform_SHA_CTX_unsafe blk_SHA_CTX
+# define platform_SHA1_Init_unsafe blk_SHA1_Init
+# define platform_SHA1_Update_unsafe blk_SHA1_Update
+# define platform_SHA1_Final_unsafe blk_SHA1_Final
+#endif
+
#if defined(SHA256_NETTLE)
#include "sha256/nettle.h"
#elif defined(SHA256_GCRYPT)
@@ -44,14 +74,32 @@
#define platform_SHA1_Final SHA1_Final
#endif
+#ifndef platform_SHA_CTX_unsafe
+# define platform_SHA_CTX_unsafe platform_SHA_CTX
+# define platform_SHA1_Init_unsafe platform_SHA1_Init
+# define platform_SHA1_Update_unsafe platform_SHA1_Update
+# define platform_SHA1_Final_unsafe platform_SHA1_Final
+# ifdef platform_SHA1_Clone
+# define platform_SHA1_Clone_unsafe platform_SHA1_Clone
+# endif
+#endif
+
#define git_SHA_CTX platform_SHA_CTX
#define git_SHA1_Init platform_SHA1_Init
#define git_SHA1_Update platform_SHA1_Update
#define git_SHA1_Final platform_SHA1_Final
+#define git_SHA_CTX_unsafe platform_SHA_CTX_unsafe
+#define git_SHA1_Init_unsafe platform_SHA1_Init_unsafe
+#define git_SHA1_Update_unsafe platform_SHA1_Update_unsafe
+#define git_SHA1_Final_unsafe platform_SHA1_Final_unsafe
+
#ifdef platform_SHA1_Clone
#define git_SHA1_Clone platform_SHA1_Clone
#endif
+#ifdef platform_SHA1_Clone_unsafe
+# define git_SHA1_Clone_unsafe platform_SHA1_Clone_unsafe
+#endif
#ifndef platform_SHA256_CTX
#define platform_SHA256_CTX SHA256_CTX
@@ -81,6 +129,13 @@ static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
memcpy(dst, src, sizeof(*dst));
}
#endif
+#ifndef SHA1_NEEDS_CLONE_HELPER_UNSAFE
+static inline void git_SHA1_Clone_unsafe(git_SHA_CTX_unsafe *dst,
+ const git_SHA_CTX_unsafe *src)
+{
+ memcpy(dst, src, sizeof(*dst));
+}
+#endif
#ifndef SHA256_NEEDS_CLONE_HELPER
static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
@@ -178,6 +233,8 @@ enum get_oid_result {
/* A suitably aligned type for stack allocations of hash contexts. */
union git_hash_ctx {
git_SHA_CTX sha1;
+ git_SHA_CTX_unsafe sha1_unsafe;
+
git_SHA256_CTX sha256;
};
typedef union git_hash_ctx git_hash_ctx;
@@ -222,6 +279,21 @@ struct git_hash_algo {
/* The hash finalization function for object IDs. */
git_hash_final_oid_fn final_oid_fn;
+ /* The non-cryptographic hash initialization function. */
+ git_hash_init_fn unsafe_init_fn;
+
+ /* The non-cryptographic hash context cloning function. */
+ git_hash_clone_fn unsafe_clone_fn;
+
+ /* The non-cryptographic hash update function. */
+ git_hash_update_fn unsafe_update_fn;
+
+ /* The non-cryptographic hash finalization function. */
+ git_hash_final_fn unsafe_final_fn;
+
+ /* The non-cryptographic hash finalization function. */
+ git_hash_final_oid_fn unsafe_final_oid_fn;
+
/* The OID of the empty tree. */
const struct object_id *empty_tree;
diff --git a/object-file.c b/object-file.c
index 7ac9533ab1..b1a3463852 100644
--- a/object-file.c
+++ b/object-file.c
@@ -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:
diff --git a/object-file.h b/object-file.h
index d6414610f8..81b30d269c 100644
--- a/object-file.h
+++ b/object-file.h
@@ -117,7 +117,13 @@ int check_object_signature(struct repository *r, const struct object_id *oid,
*/
int stream_object_signature(struct repository *r, const struct object_id *oid);
+enum finalize_object_file_flags {
+ FOF_SKIP_COLLISION_CHECK = 1,
+};
+
int finalize_object_file(const char *tmpfile, const char *filename);
+int finalize_object_file_flags(const char *tmpfile, const char *filename,
+ enum finalize_object_file_flags flags);
/* Helper to check and "touch" a file */
int check_and_freshen_file(const char *fn, int freshen);
diff --git a/pack-write.c b/pack-write.c
index 27965672f1..f415604c15 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -8,6 +8,7 @@
#include "csum-file.h"
#include "remote.h"
#include "chunk-format.h"
+#include "object-file.h"
#include "pack-mtimes.h"
#include "pack-objects.h"
#include "pack-revindex.h"
@@ -528,9 +529,9 @@ static void rename_tmp_packfile(struct strbuf *name_prefix, const char *source,
size_t name_prefix_len = name_prefix->len;
strbuf_addstr(name_prefix, ext);
- if (rename(source, name_prefix->buf))
- die_errno("unable to rename temporary file to '%s'",
- name_prefix->buf);
+ if (finalize_object_file(source, name_prefix->buf))
+ die("unable to rename temporary file to '%s'",
+ name_prefix->buf);
strbuf_setlen(name_prefix, name_prefix_len);
}
diff --git a/sha1/openssl.h b/sha1/openssl.h
index 006c1f4ba5..1038af47da 100644
--- a/sha1/openssl.h
+++ b/sha1/openssl.h
@@ -40,10 +40,12 @@ static inline void openssl_SHA1_Clone(struct openssl_SHA1_CTX *dst,
EVP_MD_CTX_copy_ex(dst->ectx, src->ectx);
}
+#ifndef platform_SHA_CTX
#define platform_SHA_CTX openssl_SHA1_CTX
#define platform_SHA1_Init openssl_SHA1_Init
#define platform_SHA1_Clone openssl_SHA1_Clone
#define platform_SHA1_Update openssl_SHA1_Update
#define platform_SHA1_Final openssl_SHA1_Final
+#endif
#endif /* SHA1_OPENSSL_H */
diff --git a/sha1dc_git.h b/sha1dc_git.h
index 60e3ce8439..f6f880cabe 100644
--- a/sha1dc_git.h
+++ b/sha1dc_git.h
@@ -18,7 +18,10 @@ void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len);
#define platform_SHA_IS_SHA1DC /* used by "test-tool sha1-is-sha1dc" */
+
+#ifndef platform_SHA_CTX
#define platform_SHA_CTX SHA1_CTX
#define platform_SHA1_Init git_SHA1DCInit
#define platform_SHA1_Update git_SHA1DCUpdate
#define platform_SHA1_Final git_SHA1DCFinal
+#endif
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 61469ef4a6..e6a43ec9ae 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -44,9 +44,14 @@ create_new_pack() {
}
do_repack() {
+ for f in $pack.*
+ do
+ mv $f "$(echo $f | sed -e 's/pack-/pack-corrupt-/')" || return 1
+ done &&
pack=$(printf "$blob_1\n$blob_2\n$blob_3\n" |
git pack-objects $@ .git/objects/pack/pack) &&
- pack=".git/objects/pack/pack-${pack}"
+ pack=".git/objects/pack/pack-${pack}" &&
+ rm -f .git/objects/pack/pack-corrupt-*
}
do_corrupt_object() {
diff --git a/tmp-objdir.c b/tmp-objdir.c
index c2fb9f9193..9da0071cba 100644
--- a/tmp-objdir.c
+++ b/tmp-objdir.c
@@ -206,9 +206,11 @@ static int read_dir_paths(struct string_list *out, const char *path)
return 0;
}
-static int migrate_paths(struct strbuf *src, struct strbuf *dst);
+static int migrate_paths(struct strbuf *src, struct strbuf *dst,
+ enum finalize_object_file_flags flags);
-static int migrate_one(struct strbuf *src, struct strbuf *dst)
+static int migrate_one(struct strbuf *src, struct strbuf *dst,
+ enum finalize_object_file_flags flags)
{
struct stat st;
@@ -220,12 +222,18 @@ static int migrate_one(struct strbuf *src, struct strbuf *dst)
return -1;
} else if (errno != EEXIST)
return -1;
- return migrate_paths(src, dst);
+ return migrate_paths(src, dst, flags);
}
- return finalize_object_file(src->buf, dst->buf);
+ return finalize_object_file_flags(src->buf, dst->buf, flags);
}
-static int migrate_paths(struct strbuf *src, struct strbuf *dst)
+static int is_loose_object_shard(const char *name)
+{
+ return strlen(name) == 2 && isxdigit(name[0]) && isxdigit(name[1]);
+}
+
+static int migrate_paths(struct strbuf *src, struct strbuf *dst,
+ enum finalize_object_file_flags flags)
{
size_t src_len = src->len, dst_len = dst->len;
struct string_list paths = STRING_LIST_INIT_DUP;
@@ -239,11 +247,15 @@ static int migrate_paths(struct strbuf *src, struct strbuf *dst)
for (i = 0; i < paths.nr; i++) {
const char *name = paths.items[i].string;
+ enum finalize_object_file_flags flags_copy = flags;
strbuf_addf(src, "/%s", name);
strbuf_addf(dst, "/%s", name);
- ret |= migrate_one(src, dst);
+ if (is_loose_object_shard(name))
+ flags_copy |= FOF_SKIP_COLLISION_CHECK;
+
+ ret |= migrate_one(src, dst, flags_copy);
strbuf_setlen(src, src_len);
strbuf_setlen(dst, dst_len);
@@ -271,7 +283,7 @@ int tmp_objdir_migrate(struct tmp_objdir *t)
strbuf_addbuf(&src, &t->path);
strbuf_addstr(&dst, repo_get_object_directory(the_repository));
- ret = migrate_paths(&src, &dst);
+ ret = migrate_paths(&src, &dst, 0);
strbuf_release(&src);
strbuf_release(&dst);