indexer.c 8.43 KB
Newer Older
1
#include "clar_libgit2.h"
2
#include <git2.h>
3 4 5 6 7 8
#include "fileops.h"
#include "hash.h"
#include "iterator.h"
#include "vector.h"
#include "posix.h"

9

10 11 12 13
/*
 * This is a packfile with three objects. The second is a delta which
 * depends on the third, which is also a delta.
 */
14
static const unsigned char out_of_order_pack[] = {
15 16 17 18 19 20 21 22 23 24 25
  0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
  0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76,
  0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10,
  0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62,
  0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x75, 0x01,
  0xd7, 0x71, 0x36, 0x66, 0xf4, 0xde, 0x82, 0x27, 0x76, 0xc7, 0x62, 0x2c,
  0x10, 0xf1, 0xb0, 0x7d, 0xe2, 0x80, 0xdc, 0x78, 0x9c, 0x63, 0x62, 0x62,
  0x62, 0xb7, 0x03, 0x00, 0x00, 0x69, 0x00, 0x4c, 0xde, 0x7d, 0xaa, 0xe4,
  0x19, 0x87, 0x58, 0x80, 0x61, 0x09, 0x9a, 0x33, 0xca, 0x7a, 0x31, 0x92,
  0x6f, 0xae, 0x66, 0x75
};
26
static const unsigned int out_of_order_pack_len = 112;
27

28 29 30 31
/*
 * Packfile with two objects. The second is a delta against an object
 * which is not in the packfile
 */
32
static const unsigned char thin_pack[] = {
33 34 35 36 37 38 39 40
  0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
  0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76,
  0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10,
  0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62,
  0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x42, 0x52,
  0x3a, 0x6f, 0x39, 0xd1, 0xfe, 0x66, 0x68, 0x6b, 0xa5, 0xe5, 0xe2, 0x97,
  0xac, 0x94, 0x6c, 0x76, 0x0b, 0x04
};
41
static const unsigned int thin_pack_len = 78;
42

43
/*
lhchavez committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
 * Packfile with one object. It references an object which is not in the
 * packfile and has a corrupt length (states the deltified stream is 1 byte
 * long, where it is actually 6).
 */
static const unsigned char corrupt_thin_pack[] = {
  0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
  0x71, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95,
  0x10, 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63,
  0x62, 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x07,
  0x67, 0x03, 0xc5, 0x40, 0x99, 0x49, 0xb1, 0x3b, 0x7d, 0xae, 0x9b, 0x0e,
  0xdd, 0xde, 0xc6, 0x76, 0x43, 0x24, 0x64
};
static const unsigned int corrupt_thin_pack_len = 67;

/*
 * Packfile with a missing trailer.
 */
static const unsigned char missing_trailer_pack[] = {
  0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x50, 0xf4, 0x3b,
};
static const unsigned int missing_trailer_pack_len = 12;

/*
67 68 69 70 71 72 73 74 75 76
 * Packfile that causes the packfile stream to open in a way in which it leaks
 * the stream reader.
 */
static const unsigned char leaky_pack[] = {
	0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03,
	0xf4, 0xbd, 0x51, 0x51, 0x51, 0x51, 0x51, 0x72, 0x65, 0x41, 0x4b, 0x63,
	0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, 0xbd, 0x41, 0x4b
};
static const unsigned int leaky_pack_len = 33;

77 78
static const unsigned char base_obj[] = { 07, 076 };
static const unsigned int base_obj_len = 2;
79

80 81
void test_pack_indexer__out_of_order(void)
{
82 83
	git_indexer *idx = 0;
	git_transfer_progress stats = { 0 };
84

85
	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
86 87
	cl_git_pass(git_indexer_append(
		idx, out_of_order_pack, out_of_order_pack_len, &stats));
88
	cl_git_pass(git_indexer_commit(idx, &stats));
89 90 91 92 93

	cl_assert_equal_i(stats.total_objects, 3);
	cl_assert_equal_i(stats.received_objects, 3);
	cl_assert_equal_i(stats.indexed_objects, 3);

94
	git_indexer_free(idx);
95
}
96

lhchavez committed
97
void test_pack_indexer__missing_trailer(void)
98 99 100 101 102 103
{
	git_indexer *idx = 0;
	git_transfer_progress stats = { 0 };

	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
	cl_git_pass(git_indexer_append(
lhchavez committed
104
		idx, missing_trailer_pack, missing_trailer_pack_len, &stats));
105 106 107 108 109 110 111 112
	cl_git_fail(git_indexer_commit(idx, &stats));

	cl_assert(giterr_last() != NULL);
	cl_assert_equal_i(giterr_last()->klass, GITERR_INDEXER);

	git_indexer_free(idx);
}

lhchavez committed
113
void test_pack_indexer__leaky(void)
114 115 116 117 118 119
{
	git_indexer *idx = 0;
	git_transfer_progress stats = { 0 };

	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
	cl_git_pass(git_indexer_append(
lhchavez committed
120
		idx, leaky_pack, leaky_pack_len, &stats));
121 122 123 124 125 126 127 128
	cl_git_fail(git_indexer_commit(idx, &stats));

	cl_assert(giterr_last() != NULL);
	cl_assert_equal_i(giterr_last()->klass, GITERR_INDEXER);

	git_indexer_free(idx);
}

129 130
void test_pack_indexer__fix_thin(void)
{
131 132
	git_indexer *idx = NULL;
	git_transfer_progress stats = { 0 };
133 134 135 136 137 138 139 140 141 142
	git_repository *repo;
	git_odb *odb;
	git_oid id, should_id;

	cl_git_pass(git_repository_init(&repo, "thin.git", true));
	cl_git_pass(git_repository_odb(&odb, repo));

	/* Store the missing base into your ODB so the indexer can fix the pack */
	cl_git_pass(git_odb_write(&id, odb, base_obj, base_obj_len, GIT_OBJ_BLOB));
	git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18");
143
	cl_assert_equal_oid(&should_id, &id);
144

145
	cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
146 147
	cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats));
	cl_git_pass(git_indexer_commit(idx, &stats));
148

149
	cl_assert_equal_i(stats.total_objects, 2);
150 151 152 153
	cl_assert_equal_i(stats.received_objects, 2);
	cl_assert_equal_i(stats.indexed_objects, 2);
	cl_assert_equal_i(stats.local_objects, 1);

154
	git_oid_fromstr(&should_id, "fefdb2d740a3a6b6c03a0c7d6ce431c6d5810e13");
155
	cl_assert_equal_oid(&should_id, git_indexer_hash(idx));
156

157
	git_indexer_free(idx);
158 159 160 161 162 163 164 165 166 167 168 169 170
	git_odb_free(odb);
	git_repository_free(repo);

	/*
	 * The pack's name/hash only tells us what objects there are,
	 * so we need to go through the packfile again in order to
	 * figure out whether we calculated the trailer correctly.
	 */
	{
		unsigned char buffer[128];
		int fd;
		ssize_t read;
		struct stat st;
171
		const char *name = "pack-fefdb2d740a3a6b6c03a0c7d6ce431c6d5810e13.pack";
172 173 174 175 176 177

		fd = p_open(name, O_RDONLY);
		cl_assert(fd != -1);

		cl_git_pass(p_stat(name, &st));

178
		cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
179 180 181 182
		read = p_read(fd, buffer, sizeof(buffer));
		cl_assert(read != -1);
		p_close(fd);

183 184
		cl_git_pass(git_indexer_append(idx, buffer, read, &stats));
		cl_git_pass(git_indexer_commit(idx, &stats));
185 186 187 188 189 190

		cl_assert_equal_i(stats.total_objects, 3);
		cl_assert_equal_i(stats.received_objects, 3);
		cl_assert_equal_i(stats.indexed_objects, 3);
		cl_assert_equal_i(stats.local_objects, 0);

191
		git_indexer_free(idx);
192 193
	}
}
lhchavez committed
194

lhchavez committed
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
void test_pack_indexer__corrupt_length(void)
{
	git_indexer *idx = NULL;
	git_transfer_progress stats = { 0 };
	git_repository *repo;
	git_odb *odb;
	git_oid id, should_id;

	cl_git_pass(git_repository_init(&repo, "thin.git", true));
	cl_git_pass(git_repository_odb(&odb, repo));

	/* Store the missing base into your ODB so the indexer can fix the pack */
	cl_git_pass(git_odb_write(&id, odb, base_obj, base_obj_len, GIT_OBJ_BLOB));
	git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18");
	cl_assert_equal_oid(&should_id, &id);

	cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
	cl_git_pass(git_indexer_append(
		idx, corrupt_thin_pack, corrupt_thin_pack_len, &stats));
	cl_git_fail(git_indexer_commit(idx, &stats));

	cl_assert(giterr_last() != NULL);
	cl_assert_equal_i(giterr_last()->klass, GITERR_ZLIB);

	git_indexer_free(idx);
	git_odb_free(odb);
	git_repository_free(repo);
}

lhchavez committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
static int find_tmp_file_recurs(void *opaque, git_buf *path)
{
	int error = 0;
	git_buf *first_tmp_file = opaque;
	struct stat st;

	if ((error = p_lstat_posixly(path->ptr, &st)) < 0)
		return error;

	if (S_ISDIR(st.st_mode))
		return git_path_direach(path, 0, find_tmp_file_recurs, opaque);

	/* This is the template that's used in git_futils_mktmp. */
	if (strstr(git_buf_cstr(path), "_git2_") != NULL)
		return git_buf_sets(first_tmp_file, git_buf_cstr(path));

	return 0;
}

void test_pack_indexer__no_tmp_files(void)
{
	git_indexer *idx = NULL;
	git_buf path = GIT_BUF_INIT;
	git_buf first_tmp_file = GIT_BUF_INIT;

	/* Precondition: there are no temporary files. */
	cl_git_pass(git_buf_sets(&path, clar_sandbox_path()));
	cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path));
lhchavez committed
252
	git_buf_free(&path);
lhchavez committed
253
	cl_assert(git_buf_len(&first_tmp_file) == 0);
lhchavez committed
254 255 256 257

	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
	git_indexer_free(idx);

lhchavez committed
258
	cl_git_pass(git_buf_sets(&path, clar_sandbox_path()));
lhchavez committed
259
	cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path));
lhchavez committed
260
	git_buf_free(&path);
lhchavez committed
261 262
	cl_assert(git_buf_len(&first_tmp_file) == 0);
	git_buf_free(&first_tmp_file);
lhchavez committed
263
}