261 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
| #include "git-compat-util.h"
 | |
| #include "hash.h"
 | |
| #include "path.h"
 | |
| #include "odb.h"
 | |
| #include "hex.h"
 | |
| #include "repository.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 odb_source *source,
 | |
| 			    const struct object_id *oid,
 | |
| 			    const struct object_id *compat_oid)
 | |
| {
 | |
| 	struct loose_object_map *map = source->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(source->loose_objects_cache, compat_oid);
 | |
| 
 | |
| 	return inserted;
 | |
| }
 | |
| 
 | |
| static int load_one_loose_object_map(struct repository *repo, struct odb_source *source)
 | |
| {
 | |
| 	struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT;
 | |
| 	FILE *fp;
 | |
| 
 | |
| 	if (!source->loose_map)
 | |
| 		loose_object_map_init(&source->loose_map);
 | |
| 	if (!source->loose_objects_cache) {
 | |
| 		ALLOC_ARRAY(source->loose_objects_cache, 1);
 | |
| 		oidtree_init(source->loose_objects_cache);
 | |
| 	}
 | |
| 
 | |
| 	insert_loose_map(source, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree);
 | |
| 	insert_loose_map(source, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob);
 | |
| 	insert_loose_map(source, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid);
 | |
| 
 | |
| 	repo_common_path_replace(repo, &path, "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(source, &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 odb_source *source;
 | |
| 
 | |
| 	if (!should_use_loose_object_map(repo))
 | |
| 		return 0;
 | |
| 
 | |
| 	odb_prepare_alternates(repo->objects);
 | |
| 
 | |
| 	for (source = repo->objects->sources; source; source = source->next) {
 | |
| 		if (load_one_loose_object_map(repo, source) < 0) {
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int repo_write_loose_object_map(struct repository *repo)
 | |
| {
 | |
| 	kh_oid_map_t *map = repo->objects->sources->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;
 | |
| 
 | |
| 	repo_common_path_replace(repo, &path, "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), repo->hash_algo->empty_tree) ||
 | |
| 			    oideq(&kh_key(map, iter), repo->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"), 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;
 | |
| 
 | |
| 	repo_common_path_replace(repo, &path, "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(repo, path.buf);
 | |
| 	rollback_lock_file(&lock);
 | |
| 	strbuf_release(&buf);
 | |
| 	strbuf_release(&path);
 | |
| 	return 0;
 | |
| errout:
 | |
| 	error_errno(_("failed to write loose object index %s"), 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->sources, 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 odb_source *source;
 | |
| 	kh_oid_map_t *map;
 | |
| 	khiter_t pos;
 | |
| 
 | |
| 	for (source = repo->objects->sources; source; source = source->next) {
 | |
| 		struct loose_object_map *loose_map = source->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;
 | |
| }
 |