blob: f6faa6216a0812b7909a034e8abac5ee00e355c2 [file] [log] [blame]
#include "git-compat-util.h"
#include "hash.h"
#include "path.h"
#include "object-store.h"
#include "hex.h"
#include "wrapper.h"
#include "gettext.h"
#include "loose.h"
#include "lockfile.h"
#include "oidtree.h"
static const char *loose_object_header = "# loose-object-idx\n";
static inline int should_use_loose_object_map(struct repository *repo)
{
return repo->compat_hash_algo && repo->gitdir;
}
void loose_object_map_init(struct loose_object_map **map)
{
struct loose_object_map *m;
m = xmalloc(sizeof(**map));
m->to_compat = kh_init_oid_map();
m->to_storage = kh_init_oid_map();
*map = m;
}
static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const struct object_id *value)
{
khiter_t pos;
int ret;
struct object_id *stored;
pos = kh_put_oid_map(map, *key, &ret);
/* This item already exists in the map. */
if (ret == 0)
return 0;
stored = xmalloc(sizeof(*stored));
oidcpy(stored, value);
kh_value(map, pos) = stored;
return 1;
}
static int insert_loose_map(struct object_directory *odb,
const struct object_id *oid,
const struct object_id *compat_oid)
{
struct loose_object_map *map = odb->loose_map;
int inserted = 0;
inserted |= insert_oid_pair(map->to_compat, oid, compat_oid);
inserted |= insert_oid_pair(map->to_storage, compat_oid, oid);
if (inserted)
oidtree_insert(odb->loose_objects_cache, compat_oid);
return inserted;
}
static int load_one_loose_object_map(struct repository *repo, struct object_directory *dir)
{
struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
FILE *fp;
if (!dir->loose_map)
loose_object_map_init(&dir->loose_map);
if (!dir->loose_objects_cache) {
ALLOC_ARRAY(dir->loose_objects_cache, 1);
oidtree_init(dir->loose_objects_cache);
}
insert_loose_map(dir, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
fp = fopen(path.buf, "rb");
if (!fp) {
strbuf_release(&path);
return 0;
}
errno = 0;
if (strbuf_getwholeline(&buf, fp, '\n') || strcmp(buf.buf, loose_object_header))
goto err;
while (!strbuf_getline_lf(&buf, fp)) {
const char *p;
struct object_id oid, compat_oid;
if (parse_oid_hex_algop(buf.buf, &oid, &p, repo->hash_algo) ||
*p++ != ' ' ||
parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) ||
p != buf.buf + buf.len)
goto err;
insert_loose_map(dir, &oid, &compat_oid);
}
strbuf_release(&buf);
strbuf_release(&path);
return errno ? -1 : 0;
err:
strbuf_release(&buf);
strbuf_release(&path);
return -1;
}
int repo_read_loose_object_map(struct repository *repo)
{
struct object_directory *dir;
if (!should_use_loose_object_map(repo))
return 0;
prepare_alt_odb(repo);
for (dir = repo->objects->odb; dir; dir = dir->next) {
if (load_one_loose_object_map(repo, dir) < 0) {
return -1;
}
}
return 0;
}
int repo_write_loose_object_map(struct repository *repo)
{
kh_oid_map_t *map = repo->objects->odb->loose_map->to_compat;
struct lock_file lock;
int fd;
khiter_t iter;
struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
if (!should_use_loose_object_map(repo))
return 0;
strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
fd = hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
iter = kh_begin(map);
if (write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
goto errout;
for (; iter != kh_end(map); iter++) {
if (kh_exist(map, iter)) {
if (oideq(&kh_key(map, iter), the_hash_algo->empty_tree) ||
oideq(&kh_key(map, iter), the_hash_algo->empty_blob))
continue;
strbuf_addf(&buf, "%s %s\n", oid_to_hex(&kh_key(map, iter)), oid_to_hex(kh_value(map, iter)));
if (write_in_full(fd, buf.buf, buf.len) < 0)
goto errout;
strbuf_reset(&buf);
}
}
strbuf_release(&buf);
if (commit_lock_file(&lock) < 0) {
error_errno(_("could not write loose object index %s"), path.buf);
strbuf_release(&path);
return -1;
}
strbuf_release(&path);
return 0;
errout:
rollback_lock_file(&lock);
strbuf_release(&buf);
error_errno(_("failed to write loose object index %s\n"), path.buf);
strbuf_release(&path);
return -1;
}
static int write_one_object(struct repository *repo, const struct object_id *oid,
const struct object_id *compat_oid)
{
struct lock_file lock;
int fd;
struct stat st;
struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
strbuf_git_common_path(&path, repo, "objects/loose-object-idx");
hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1);
fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666);
if (fd < 0)
goto errout;
if (fstat(fd, &st) < 0)
goto errout;
if (!st.st_size && write_in_full(fd, loose_object_header, strlen(loose_object_header)) < 0)
goto errout;
strbuf_addf(&buf, "%s %s\n", oid_to_hex(oid), oid_to_hex(compat_oid));
if (write_in_full(fd, buf.buf, buf.len) < 0)
goto errout;
if (close(fd))
goto errout;
adjust_shared_perm(path.buf);
rollback_lock_file(&lock);
strbuf_release(&buf);
strbuf_release(&path);
return 0;
errout:
error_errno(_("failed to write loose object index %s\n"), path.buf);
close(fd);
rollback_lock_file(&lock);
strbuf_release(&buf);
strbuf_release(&path);
return -1;
}
int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid,
const struct object_id *compat_oid)
{
int inserted = 0;
if (!should_use_loose_object_map(repo))
return 0;
inserted = insert_loose_map(repo->objects->odb, oid, compat_oid);
if (inserted)
return write_one_object(repo, oid, compat_oid);
return 0;
}
int repo_loose_object_map_oid(struct repository *repo,
const struct object_id *src,
const struct git_hash_algo *to,
struct object_id *dest)
{
struct object_directory *dir;
kh_oid_map_t *map;
khiter_t pos;
for (dir = repo->objects->odb; dir; dir = dir->next) {
struct loose_object_map *loose_map = dir->loose_map;
if (!loose_map)
continue;
map = (to == repo->compat_hash_algo) ?
loose_map->to_compat :
loose_map->to_storage;
pos = kh_get_oid_map(map, *src);
if (pos < kh_end(map)) {
oidcpy(dest, kh_value(map, pos));
return 0;
}
}
return -1;
}
void loose_object_map_clear(struct loose_object_map **map)
{
struct loose_object_map *m = *map;
struct object_id *oid;
if (!m)
return;
kh_foreach_value(m->to_compat, oid, free(oid));
kh_foreach_value(m->to_storage, oid, free(oid));
kh_destroy_oid_map(m->to_compat);
kh_destroy_oid_map(m->to_storage);
free(m);
*map = NULL;
}