filemodes.c 9.76 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#include "clar_libgit2.h"
#include "posix.h"
#include "index.h"

static git_repository *g_repo = NULL;

void test_index_filemodes__initialize(void)
{
	g_repo = cl_git_sandbox_init("filemodes");
}

void test_index_filemodes__cleanup(void)
{
	cl_git_sandbox_cleanup();
}

void test_index_filemodes__read(void)
{
	git_index *index;
	unsigned int i;
	static bool expected[6] = { 0, 1, 0, 1, 0, 1 };

	cl_git_pass(git_repository_index(&index, g_repo));
24
	cl_assert_equal_i(6, (int)git_index_entrycount(index));
25 26

	for (i = 0; i < 6; ++i) {
Ben Straub committed
27
		const git_index_entry *entry = git_index_get_byindex(index, i);
28 29 30 31 32 33 34 35 36 37
		cl_assert(entry != NULL);
		cl_assert(((entry->mode & 0100) ? 1 : 0) == expected[i]);
	}

	git_index_free(index);
}

static void replace_file_with_mode(
	const char *filename, const char *backup, unsigned int create_mode)
{
38
	git_str path = GIT_STR_INIT, content = GIT_STR_INIT;
39

40 41
	cl_git_pass(git_str_joinpath(&path, "filemodes", filename));
	cl_git_pass(git_str_printf(&content, "%s as %08u (%d)",
42 43 44 45
		filename, create_mode, rand()));

	cl_git_pass(p_rename(path.ptr, backup));
	cl_git_write2file(
46 47
		path.ptr, content.ptr, content.size,
		O_WRONLY|O_CREAT|O_TRUNC, create_mode);
48

49 50
	git_str_dispose(&path);
	git_str_dispose(&content);
51 52
}

53
#define add_and_check_mode(I,F,X) add_and_check_mode_(I,F,X,__FILE__,__func__,__LINE__)
54 55 56

static void add_and_check_mode_(
	git_index *index, const char *filename, unsigned int expect_mode,
57
	const char *file, const char *func, int line)
58
{
59
	size_t pos;
Ben Straub committed
60
	const git_index_entry *entry;
61

62
	cl_git_pass(git_index_add_bypath(index, filename));
63

64
	clar__assert(!git_index_find(&pos, index, filename),
65
		file, func, line, "Cannot find index entry", NULL, 1);
66

Edward Thomson committed
67
	entry = git_index_get_byindex(index, pos);
68

69
	clar__assert_equal(file, func, line, "Expected mode does not match index",
70
		1, "%07o", (unsigned int)entry->mode, (unsigned int)expect_mode);
71 72 73 74 75 76
}

void test_index_filemodes__untrusted(void)
{
	git_index *index;

77
	cl_repo_set_bool(g_repo, "core.filemode", false);
78 79

	cl_git_pass(git_repository_index(&index, g_repo));
80
	cl_assert((git_index_caps(index) & GIT_INDEX_CAPABILITY_NO_FILEMODE) != 0);
81 82 83

	/* 1 - add 0644 over existing 0644 -> expect 0644 */
	replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
nulltoken committed
84
	add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
85 86 87

	/* 2 - add 0644 over existing 0755 -> expect 0755 */
	replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
nulltoken committed
88
	add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
89 90 91

	/* 3 - add 0755 over existing 0644 -> expect 0644 */
	replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
nulltoken committed
92
	add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
93 94 95

	/* 4 - add 0755 over existing 0755 -> expect 0755 */
	replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
nulltoken committed
96
	add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
97

98 99 100 101 102 103 104 105 106
	/*  5 - add new 0644 -> expect 0644 */
	cl_git_write2file("filemodes/new_off", "blah", 0,
		O_WRONLY | O_CREAT | O_TRUNC, 0644);
	add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);

	/* 6 - add new 0755 -> expect 0644 if core.filemode == false */
	cl_git_write2file("filemodes/new_on", "blah", 0,
		O_WRONLY | O_CREAT | O_TRUNC, 0755);
	add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB);
107 108 109 110 111 112 113 114 115 116 117 118 119 120

	git_index_free(index);
}

void test_index_filemodes__trusted(void)
{
	git_index *index;

	/* Only run these tests on platforms where I can actually
	 * chmod a file and get the stat results I expect!
	 */
	if (!cl_is_chmod_supported())
		return;

121
	cl_repo_set_bool(g_repo, "core.filemode", true);
122 123

	cl_git_pass(git_repository_index(&index, g_repo));
124
	cl_assert((git_index_caps(index) & GIT_INDEX_CAPABILITY_NO_FILEMODE) == 0);
125 126 127

	/* 1 - add 0644 over existing 0644 -> expect 0644 */
	replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
nulltoken committed
128
	add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
129 130 131

	/* 2 - add 0644 over existing 0755 -> expect 0644 */
	replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
nulltoken committed
132
	add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB);
133 134 135

	/* 3 - add 0755 over existing 0644 -> expect 0755 */
	replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
nulltoken committed
136
	add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB_EXECUTABLE);
137 138 139

	/* 4 - add 0755 over existing 0755 -> expect 0755 */
	replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
nulltoken committed
140
	add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
141

Edward Thomson committed
142
	/*  5 - add new 0644 -> expect 0644 */
143
	cl_git_write2file("filemodes/new_off", "blah", 0,
144
		O_WRONLY | O_CREAT | O_TRUNC, 0644);
nulltoken committed
145
	add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
146

Edward Thomson committed
147
	/* 6 - add 0755 -> expect 0755 */
148
	cl_git_write2file("filemodes/new_on", "blah", 0,
149
		O_WRONLY | O_CREAT | O_TRUNC, 0755);
nulltoken committed
150
	add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
151 152 153

	git_index_free(index);
}
154

155
#define add_entry_and_check_mode(I,FF,X) add_entry_and_check_mode_(I,FF,X,__FILE__,__func__,__LINE__)
156 157 158

static void add_entry_and_check_mode_(
	git_index *index, bool from_file, git_filemode_t mode,
159
	const char *file, const char *func, int line)
160 161 162 163 164 165
{
	size_t pos;
	const git_index_entry* entry;
	git_index_entry new_entry;

	/* If old_filename exists, we copy that to the new file, and test
166
	 * git_index_add(), otherwise create a new entry testing git_index_add_from_buffer
167 168 169 170
	 */
	if (from_file)
	{
		clar__assert(!git_index_find(&pos, index, "exec_off"),
171
			file, func, line, "Cannot find original index entry", NULL, 1);
172 173 174 175 176 177 178 179 180 181 182 183 184 185

		entry = git_index_get_byindex(index, pos);

		memcpy(&new_entry, entry, sizeof(new_entry));
	}
	else
		memset(&new_entry, 0x0, sizeof(git_index_entry));

	new_entry.path = "filemodes/explicit_test";
	new_entry.mode = mode;

	if (from_file)
	{
		clar__assert(!git_index_add(index, &new_entry),
186
			file, func, line, "Cannot add index entry", NULL, 1);
187 188 189 190
	}
	else
	{
		const char *content = "hey there\n";
191
		clar__assert(!git_index_add_from_buffer(index, &new_entry, content, strlen(content)),
192
			file, func, line, "Cannot add index entry from buffer", NULL, 1);
193 194 195
	}

	clar__assert(!git_index_find(&pos, index, "filemodes/explicit_test"),
196
		file, func, line, "Cannot find new index entry", NULL, 1);
197 198 199

	entry = git_index_get_byindex(index, pos);

200
	clar__assert_equal(file, func, line, "Expected mode does not match index",
201 202 203 204 205 206 207 208
		1, "%07o", (unsigned int)entry->mode, (unsigned int)mode);
}

void test_index_filemodes__explicit(void)
{
	git_index *index;

	/* These tests should run and work everywhere, as the filemode is
209
	 * given explicitly to git_index_add or git_index_add_from_buffer
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
	 */
	cl_repo_set_bool(g_repo, "core.filemode", false);

	cl_git_pass(git_repository_index(&index, g_repo));

	/* Each of these tests keeps overwriting the same file in the index. */
	/* 1 - add new 0644 entry  */
	add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB);

	/* 2 - add 0755 entry over existing 0644 */
	add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB_EXECUTABLE);

	/* 3 - add 0644 entry over existing 0755 */
	add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB);

	/* 4 - add 0755 buffer entry over existing 0644  */
	add_entry_and_check_mode(index, false, GIT_FILEMODE_BLOB_EXECUTABLE);

	/* 5 - add 0644 buffer entry over existing 0755 */
	add_entry_and_check_mode(index, false, GIT_FILEMODE_BLOB);

	git_index_free(index);
}

234 235 236 237
void test_index_filemodes__invalid(void)
{
	git_index *index;
	git_index_entry entry;
238
	const git_index_entry *dummy;
239 240 241

	cl_git_pass(git_repository_index(&index, g_repo));

242 243 244 245 246
	/* add a dummy file so that we have a valid id */
	cl_git_mkfile("./filemodes/dummy-file.txt", "new-file\n");
	cl_git_pass(git_index_add_bypath(index, "dummy-file.txt"));
	cl_assert((dummy = git_index_get_bypath(index, "dummy-file.txt", 0)));

247
	GIT_INDEX_ENTRY_STAGE_SET(&entry, 0);
248
	entry.path = "foo";
249
	entry.mode = GIT_OBJECT_BLOB;
250
	git_oid_cpy(&entry.id, &dummy->id);
251 252 253 254
	cl_git_fail(git_index_add(index, &entry));

	entry.mode = GIT_FILEMODE_BLOB;
	cl_git_pass(git_index_add(index, &entry));
255 256

	git_index_free(index);
257
}
258 259 260 261 262 263 264 265

void test_index_filemodes__frombuffer_requires_files(void)
{
	git_index *index;
	git_index_entry new_entry;
	const git_index_entry *ret_entry;
	const char *content = "hey there\n";

266
	memset(&new_entry, 0, sizeof(new_entry));
267 268 269 270 271 272
	cl_git_pass(git_repository_index(&index, g_repo));

	/* regular blob */
	new_entry.path = "dummy-file.txt";
	new_entry.mode = GIT_FILEMODE_BLOB;

273
	cl_git_pass(git_index_add_from_buffer(index,
274 275 276 277 278 279 280 281 282 283
		&new_entry, content, strlen(content)));

	cl_assert((ret_entry = git_index_get_bypath(index, "dummy-file.txt", 0)));
	cl_assert_equal_s("dummy-file.txt", ret_entry->path);
	cl_assert_equal_i(GIT_FILEMODE_BLOB, ret_entry->mode);

	/* executable blob */
	new_entry.path = "dummy-file.txt";
	new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;

284
	cl_git_pass(git_index_add_from_buffer(index,
285 286 287 288 289 290 291 292 293 294
		&new_entry, content, strlen(content)));

	cl_assert((ret_entry = git_index_get_bypath(index, "dummy-file.txt", 0)));
	cl_assert_equal_s("dummy-file.txt", ret_entry->path);
	cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, ret_entry->mode);

	/* links are also acceptable */
	new_entry.path = "dummy-link.txt";
	new_entry.mode = GIT_FILEMODE_LINK;

295
	cl_git_pass(git_index_add_from_buffer(index,
296 297 298 299 300 301 302 303 304 305
		&new_entry, content, strlen(content)));

	cl_assert((ret_entry = git_index_get_bypath(index, "dummy-link.txt", 0)));
	cl_assert_equal_s("dummy-link.txt", ret_entry->path);
	cl_assert_equal_i(GIT_FILEMODE_LINK, ret_entry->mode);

	/* trees are rejected */
	new_entry.path = "invalid_mode.txt";
	new_entry.mode = GIT_FILEMODE_TREE;

306
	cl_git_fail(git_index_add_from_buffer(index,
307 308 309 310 311 312 313
		&new_entry, content, strlen(content)));
	cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));

	/* submodules are rejected */
	new_entry.path = "invalid_mode.txt";
	new_entry.mode = GIT_FILEMODE_COMMIT;

314
	cl_git_fail(git_index_add_from_buffer(index,
315 316 317 318 319
		&new_entry, content, strlen(content)));
	cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));

	git_index_free(index);
}