@@ -1932,10 +1932,67 @@ 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;
@@ -1974,7 +2031,9 @@ int finalize_object_file(const char *tmpfile, const char *filename)
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);
}
@@ -2228,7 +2287,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)
@@ -2350,7 +2410,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:
@@ -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);
@@ -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);