]> git.proxmox.com Git - libgit2.git/blob - tests/checkout/nasty.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / tests / checkout / nasty.c
1 #include "clar_libgit2.h"
2 #include "checkout_helpers.h"
3
4 #include "git2/checkout.h"
5 #include "repository.h"
6 #include "futils.h"
7
8 static const char *repo_name = "nasty";
9 static git_repository *repo;
10 static git_checkout_options checkout_opts;
11
12 void test_checkout_nasty__initialize(void)
13 {
14 repo = cl_git_sandbox_init(repo_name);
15
16 GIT_INIT_STRUCTURE(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION);
17 checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
18 }
19
20 void test_checkout_nasty__cleanup(void)
21 {
22 cl_git_sandbox_cleanup();
23 }
24
25 static void test_checkout_passes(const char *refname, const char *filename)
26 {
27 git_oid commit_id;
28 git_commit *commit;
29 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
30 git_str path = GIT_STR_INIT;
31
32 cl_git_pass(git_str_joinpath(&path, repo_name, filename));
33
34 cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname));
35 cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
36
37 opts.checkout_strategy = GIT_CHECKOUT_FORCE |
38 GIT_CHECKOUT_DONT_UPDATE_INDEX;
39
40 cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts));
41 cl_assert(!git_fs_path_exists(path.ptr));
42
43 git_commit_free(commit);
44 git_str_dispose(&path);
45 }
46
47 static void test_checkout_fails(const char *refname, const char *filename)
48 {
49 git_oid commit_id;
50 git_commit *commit;
51 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
52 git_str path = GIT_STR_INIT;
53
54 cl_git_pass(git_str_joinpath(&path, repo_name, filename));
55
56 cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname));
57 cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
58
59 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
60
61 cl_git_fail(git_checkout_tree(repo, (const git_object *)commit, &opts));
62 cl_assert(!git_fs_path_exists(path.ptr));
63
64 git_commit_free(commit);
65 git_str_dispose(&path);
66 }
67
68 /* A tree that contains ".git" as a tree, with a blob inside
69 * (".git/foobar").
70 */
71 void test_checkout_nasty__dotgit_tree(void)
72 {
73 test_checkout_fails("refs/heads/dotgit_tree", ".git/foobar");
74 }
75
76 /* A tree that contains ".GIT" as a tree, with a blob inside
77 * (".GIT/foobar").
78 */
79 void test_checkout_nasty__dotcapitalgit_tree(void)
80 {
81 test_checkout_fails("refs/heads/dotcapitalgit_tree", ".GIT/foobar");
82 }
83
84 /* A tree that contains a tree ".", with a blob inside ("./foobar").
85 */
86 void test_checkout_nasty__dot_tree(void)
87 {
88 test_checkout_fails("refs/heads/dot_tree", "foobar");
89 }
90
91 /* A tree that contains a tree ".", with a tree ".git", with a blob
92 * inside ("./.git/foobar").
93 */
94 void test_checkout_nasty__dot_dotgit_tree(void)
95 {
96 test_checkout_fails("refs/heads/dot_dotgit_tree", ".git/foobar");
97 }
98
99 /* A tree that contains a tree, with a tree "..", with a tree ".git", with a
100 * blob inside ("foo/../.git/foobar").
101 */
102 void test_checkout_nasty__dotdot_dotgit_tree(void)
103 {
104 test_checkout_fails("refs/heads/dotdot_dotgit_tree", ".git/foobar");
105 }
106
107 /* A tree that contains a tree, with a tree "..", with a blob inside
108 * ("foo/../foobar").
109 */
110 void test_checkout_nasty__dotdot_tree(void)
111 {
112 test_checkout_fails("refs/heads/dotdot_tree", "foobar");
113 }
114
115 /* A tree that contains a blob with the rogue name ".git/foobar" */
116 void test_checkout_nasty__dotgit_path(void)
117 {
118 test_checkout_fails("refs/heads/dotgit_path", ".git/foobar");
119 }
120
121 /* A tree that contains a blob with the rogue name ".GIT/foobar" */
122 void test_checkout_nasty__dotcapitalgit_path(void)
123 {
124 test_checkout_fails("refs/heads/dotcapitalgit_path", ".GIT/foobar");
125 }
126
127 /* A tree that contains a blob with the rogue name "./.git/foobar" */
128 void test_checkout_nasty__dot_dotgit_path(void)
129 {
130 test_checkout_fails("refs/heads/dot_dotgit_path", ".git/foobar");
131 }
132
133 /* A tree that contains a blob with the rogue name "./.GIT/foobar" */
134 void test_checkout_nasty__dot_dotcapitalgit_path(void)
135 {
136 test_checkout_fails("refs/heads/dot_dotcapitalgit_path", ".GIT/foobar");
137 }
138
139 /* A tree that contains a blob with the rogue name "foo/../.git/foobar" */
140 void test_checkout_nasty__dotdot_dotgit_path(void)
141 {
142 test_checkout_fails("refs/heads/dotdot_dotgit_path", ".git/foobar");
143 }
144
145 /* A tree that contains a blob with the rogue name "foo/../.GIT/foobar" */
146 void test_checkout_nasty__dotdot_dotcapitalgit_path(void)
147 {
148 test_checkout_fails("refs/heads/dotdot_dotcapitalgit_path", ".GIT/foobar");
149 }
150
151 /* A tree that contains a blob with the rogue name "foo/." */
152 void test_checkout_nasty__dot_path(void)
153 {
154 test_checkout_fails("refs/heads/dot_path", "./foobar");
155 }
156
157 /* A tree that contains a blob with the rogue name "foo/." */
158 void test_checkout_nasty__dot_path_two(void)
159 {
160 test_checkout_fails("refs/heads/dot_path_two", "foo/.");
161 }
162
163 /* A tree that contains a blob with the rogue name "foo/../foobar" */
164 void test_checkout_nasty__dotdot_path(void)
165 {
166 test_checkout_fails("refs/heads/dotdot_path", "foobar");
167 }
168
169 /* A tree that contains an entry with a backslash ".git\foobar" */
170 void test_checkout_nasty__dotgit_backslash_path(void)
171 {
172 #ifdef GIT_WIN32
173 test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar");
174 #endif
175 }
176
177 /* A tree that contains an entry with a backslash ".GIT\foobar" */
178 void test_checkout_nasty__dotcapitalgit_backslash_path(void)
179 {
180 #ifdef GIT_WIN32
181 test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar");
182 #endif
183 }
184
185 /* A tree that contains an entry with a backslash ".\.GIT\foobar" */
186 void test_checkout_nasty__dot_backslash_dotcapitalgit_path(void)
187 {
188 #ifdef GIT_WIN32
189 test_checkout_fails("refs/heads/dot_backslash_dotcapitalgit_path", ".GIT/foobar");
190 #endif
191 }
192
193 /* A tree that contains an entry ".git.", because Win32 APIs will drop the
194 * trailing slash.
195 */
196 void test_checkout_nasty__dot_git_dot(void)
197 {
198 #ifdef GIT_WIN32
199 test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar");
200 #endif
201 }
202
203 /* A tree that contains an entry "git~1", because that is typically the
204 * short name for ".git".
205 */
206 void test_checkout_nasty__git_tilde1(void)
207 {
208 test_checkout_fails("refs/heads/git_tilde1", ".git/foobar");
209 test_checkout_fails("refs/heads/git_tilde1", "git~1/foobar");
210 }
211
212 /* A tree that contains an entry "git~2", when we have forced the short
213 * name for ".git" into "GIT~2".
214 */
215 void test_checkout_nasty__git_custom_shortname(void)
216 {
217 #ifdef GIT_WIN32
218 if (!cl_sandbox_supports_8dot3())
219 clar__skip();
220
221 cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
222 cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
223 cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
224 test_checkout_fails("refs/heads/git_tilde2", ".git/foobar");
225 #endif
226 }
227
228 /* A tree that contains an entry "git~3", which should be allowed, since
229 * it is not the typical short name ("GIT~1") or the actual short name
230 * ("GIT~2") for ".git".
231 */
232 void test_checkout_nasty__only_looks_like_a_git_shortname(void)
233 {
234 #ifdef GIT_WIN32
235 git_oid commit_id;
236 git_commit *commit;
237 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
238
239 cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
240 cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
241 cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
242
243 cl_git_pass(git_reference_name_to_id(&commit_id, repo, "refs/heads/git_tilde3"));
244 cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
245
246 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
247
248 cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts));
249 cl_assert(git_fs_path_exists("nasty/git~3/foobar"));
250
251 git_commit_free(commit);
252 #endif
253 }
254
255 /* A tree that contains an entry "git:", because Win32 APIs will reject
256 * that as looking too similar to a drive letter.
257 */
258 void test_checkout_nasty__dot_git_colon(void)
259 {
260 #ifdef GIT_WIN32
261 test_checkout_fails("refs/heads/dot_git_colon", ".git/foobar");
262 #endif
263 }
264
265 /* A tree that contains an entry "git:foo", because Win32 APIs will turn
266 * that into ".git".
267 */
268 void test_checkout_nasty__dot_git_colon_stuff(void)
269 {
270 #ifdef GIT_WIN32
271 test_checkout_fails("refs/heads/dot_git_colon_stuff", ".git/foobar");
272 #endif
273 }
274
275 /* A tree that contains an entry ".git::$INDEX_ALLOCATION" because NTFS
276 * will interpret that as a synonym to ".git", even when mounted via SMB
277 * on macOS.
278 */
279 void test_checkout_nasty__dotgit_alternate_data_stream(void)
280 {
281 test_checkout_fails("refs/heads/dotgit_alternate_data_stream", ".git/dummy-file");
282 test_checkout_fails("refs/heads/dotgit_alternate_data_stream", ".git::$INDEX_ALLOCATION/dummy-file");
283 }
284
285 /* Trees that contains entries with a tree ".git" that contain
286 * byte sequences:
287 * { 0xe2, 0x80, 0x8c }
288 * { 0xe2, 0x80, 0x8d }
289 * { 0xe2, 0x80, 0x8e }
290 * { 0xe2, 0x80, 0x8f }
291 * { 0xe2, 0x80, 0xaa }
292 * { 0xe2, 0x80, 0xab }
293 * { 0xe2, 0x80, 0xac }
294 * { 0xe2, 0x80, 0xad }
295 * { 0xe2, 0x81, 0xae }
296 * { 0xe2, 0x81, 0xaa }
297 * { 0xe2, 0x81, 0xab }
298 * { 0xe2, 0x81, 0xac }
299 * { 0xe2, 0x81, 0xad }
300 * { 0xe2, 0x81, 0xae }
301 * { 0xe2, 0x81, 0xaf }
302 * { 0xef, 0xbb, 0xbf }
303 * Because these map to characters that HFS filesystems "ignore". Thus
304 * ".git<U+200C>" will map to ".git".
305 */
306 void test_checkout_nasty__dot_git_hfs_ignorable(void)
307 {
308 #ifdef __APPLE__
309 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
310 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
311 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
312 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
313 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
314 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
315 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
316 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
317 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
318 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
319 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
320 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
321 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
322 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
323 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
324 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
325 #endif
326 }
327
328 void test_checkout_nasty__honors_core_protecthfs(void)
329 {
330 cl_repo_set_bool(repo, "core.protectHFS", true);
331
332 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
333 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
334 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
335 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
336 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
337 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
338 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
339 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
340 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
341 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
342 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
343 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
344 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
345 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
346 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
347 test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
348 }
349
350 void test_checkout_nasty__honors_core_protectntfs(void)
351 {
352 cl_repo_set_bool(repo, "core.protectNTFS", true);
353
354 test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar");
355 test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar");
356 test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar");
357 test_checkout_fails("refs/heads/git_tilde1", ".git/foobar");
358 }
359
360 void test_checkout_nasty__symlink1(void)
361 {
362 test_checkout_passes("refs/heads/symlink1", ".git/foobar");
363 }
364
365 void test_checkout_nasty__symlink2(void)
366 {
367 test_checkout_passes("refs/heads/symlink2", ".git/foobar");
368 }
369
370 void test_checkout_nasty__symlink3(void)
371 {
372 test_checkout_passes("refs/heads/symlink3", ".git/foobar");
373 }
374
375 void test_checkout_nasty__gitmodules_symlink(void)
376 {
377 cl_repo_set_bool(repo, "core.protectHFS", true);
378 test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
379 cl_repo_set_bool(repo, "core.protectHFS", false);
380
381 cl_repo_set_bool(repo, "core.protectNTFS", true);
382 test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
383 cl_repo_set_bool(repo, "core.protectNTFS", false);
384
385 test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
386 }