]>
Commit | Line | Data |
---|---|---|
65477db1 ET |
1 | #include "clar_libgit2.h" |
2 | #include "posix.h" | |
3 | #include "buffer.h" | |
4 | #include "path.h" | |
5 | ||
6 | #ifdef GIT_WIN32 | |
7 | # include "win32/reparse.h" | |
8 | #endif | |
9 | ||
10 | void test_core_link__cleanup(void) | |
11 | { | |
12 | #ifdef GIT_WIN32 | |
13 | RemoveDirectory("lstat_junction"); | |
14 | RemoveDirectory("lstat_dangling"); | |
15 | RemoveDirectory("lstat_dangling_dir"); | |
16 | RemoveDirectory("lstat_dangling_junction"); | |
17 | ||
18 | RemoveDirectory("stat_junction"); | |
19 | RemoveDirectory("stat_dangling"); | |
20 | RemoveDirectory("stat_dangling_dir"); | |
21 | RemoveDirectory("stat_dangling_junction"); | |
22 | #endif | |
23 | } | |
24 | ||
25 | #ifdef GIT_WIN32 | |
6b11eb51 | 26 | static bool should_run(void) |
65477db1 ET |
27 | { |
28 | static SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY }; | |
29 | PSID admin_sid; | |
30 | BOOL is_admin; | |
31 | ||
32 | cl_win32_pass(AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &admin_sid)); | |
33 | cl_win32_pass(CheckTokenMembership(NULL, admin_sid, &is_admin)); | |
34 | FreeSid(admin_sid); | |
35 | ||
36 | return is_admin ? true : false; | |
37 | } | |
6b11eb51 ET |
38 | #else |
39 | static bool should_run(void) | |
40 | { | |
41 | return true; | |
42 | } | |
65477db1 ET |
43 | #endif |
44 | ||
45 | static void do_symlink(const char *old, const char *new, int is_dir) | |
46 | { | |
47 | #ifndef GIT_WIN32 | |
48 | GIT_UNUSED(is_dir); | |
49 | ||
50 | cl_must_pass(symlink(old, new)); | |
51 | #else | |
52 | typedef DWORD (WINAPI *create_symlink_func)(LPCTSTR, LPCTSTR, DWORD); | |
53 | HMODULE module; | |
54 | create_symlink_func pCreateSymbolicLink; | |
55 | ||
65477db1 | 56 | cl_assert(module = GetModuleHandle("kernel32")); |
22a2d3d5 | 57 | cl_assert(pCreateSymbolicLink = (create_symlink_func)(void *)GetProcAddress(module, "CreateSymbolicLinkA")); |
65477db1 ET |
58 | |
59 | cl_win32_pass(pCreateSymbolicLink(new, old, is_dir)); | |
60 | #endif | |
61 | } | |
62 | ||
63 | static void do_hardlink(const char *old, const char *new) | |
64 | { | |
65 | #ifndef GIT_WIN32 | |
66 | cl_must_pass(link(old, new)); | |
67 | #else | |
68 | typedef DWORD (WINAPI *create_hardlink_func)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES); | |
69 | HMODULE module; | |
70 | create_hardlink_func pCreateHardLink; | |
71 | ||
65477db1 | 72 | cl_assert(module = GetModuleHandle("kernel32")); |
22a2d3d5 | 73 | cl_assert(pCreateHardLink = (create_hardlink_func)(void *)GetProcAddress(module, "CreateHardLinkA")); |
65477db1 ET |
74 | |
75 | cl_win32_pass(pCreateHardLink(new, old, 0)); | |
76 | #endif | |
77 | } | |
78 | ||
79 | #ifdef GIT_WIN32 | |
80 | ||
81 | static void do_junction(const char *old, const char *new) | |
82 | { | |
83 | GIT_REPARSE_DATA_BUFFER *reparse_buf; | |
84 | HANDLE handle; | |
85 | git_buf unparsed_buf = GIT_BUF_INIT; | |
86 | wchar_t *subst_utf16, *print_utf16; | |
87 | DWORD ioctl_ret; | |
88 | int subst_utf16_len, subst_byte_len, print_utf16_len, print_byte_len, ret; | |
89 | USHORT reparse_buflen; | |
90 | size_t i; | |
91 | ||
92 | /* Junction targets must be the unparsed name, starting with \??\, using | |
93 | * backslashes instead of forward, and end in a trailing backslash. | |
94 | * eg: \??\C:\Foo\ | |
95 | */ | |
96 | git_buf_puts(&unparsed_buf, "\\??\\"); | |
97 | ||
98 | for (i = 0; i < strlen(old); i++) | |
99 | git_buf_putc(&unparsed_buf, old[i] == '/' ? '\\' : old[i]); | |
100 | ||
101 | git_buf_putc(&unparsed_buf, '\\'); | |
102 | ||
103 | subst_utf16_len = git__utf8_to_16(NULL, 0, git_buf_cstr(&unparsed_buf)); | |
104 | subst_byte_len = subst_utf16_len * sizeof(WCHAR); | |
105 | ||
106 | print_utf16_len = subst_utf16_len - 4; | |
107 | print_byte_len = subst_byte_len - (4 * sizeof(WCHAR)); | |
108 | ||
109 | /* The junction must be an empty directory before the junction attribute | |
110 | * can be added. | |
111 | */ | |
112 | cl_win32_pass(CreateDirectoryA(new, NULL)); | |
113 | ||
114 | handle = CreateFileA(new, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, | |
115 | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); | |
116 | cl_win32_pass(handle != INVALID_HANDLE_VALUE); | |
117 | ||
118 | reparse_buflen = (USHORT)(REPARSE_DATA_HEADER_SIZE + | |
119 | REPARSE_DATA_MOUNTPOINT_HEADER_SIZE + | |
120 | subst_byte_len + sizeof(WCHAR) + | |
121 | print_byte_len + sizeof(WCHAR)); | |
122 | ||
123 | reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen); | |
124 | cl_assert(reparse_buf); | |
125 | ||
126 | subst_utf16 = reparse_buf->MountPointReparseBuffer.PathBuffer; | |
127 | print_utf16 = subst_utf16 + subst_utf16_len + 1; | |
128 | ||
129 | ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1, | |
130 | git_buf_cstr(&unparsed_buf)); | |
131 | cl_assert_equal_i(subst_utf16_len, ret); | |
132 | ||
133 | ret = git__utf8_to_16(print_utf16, | |
134 | print_utf16_len + 1, git_buf_cstr(&unparsed_buf) + 4); | |
135 | cl_assert_equal_i(print_utf16_len, ret); | |
136 | ||
137 | reparse_buf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; | |
138 | reparse_buf->MountPointReparseBuffer.SubstituteNameOffset = 0; | |
7110000d | 139 | reparse_buf->MountPointReparseBuffer.SubstituteNameLength = subst_byte_len; |
65477db1 ET |
140 | reparse_buf->MountPointReparseBuffer.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR)); |
141 | reparse_buf->MountPointReparseBuffer.PrintNameLength = print_byte_len; | |
142 | reparse_buf->ReparseDataLength = reparse_buflen - REPARSE_DATA_HEADER_SIZE; | |
143 | ||
144 | cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, | |
145 | reparse_buf, reparse_buflen, NULL, 0, &ioctl_ret, NULL)); | |
146 | ||
147 | CloseHandle(handle); | |
148 | LocalFree(reparse_buf); | |
6b11eb51 | 149 | |
ac3d33df | 150 | git_buf_dispose(&unparsed_buf); |
65477db1 ET |
151 | } |
152 | ||
153 | static void do_custom_reparse(const char *path) | |
154 | { | |
155 | REPARSE_GUID_DATA_BUFFER *reparse_buf; | |
156 | HANDLE handle; | |
157 | DWORD ioctl_ret; | |
158 | ||
159 | const char *reparse_data = "Reparse points are silly."; | |
160 | size_t reparse_buflen = REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + | |
161 | strlen(reparse_data) + 1; | |
162 | ||
163 | reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen); | |
164 | cl_assert(reparse_buf); | |
165 | ||
166 | reparse_buf->ReparseTag = 42; | |
167 | reparse_buf->ReparseDataLength = (WORD)(strlen(reparse_data) + 1); | |
168 | ||
169 | reparse_buf->ReparseGuid.Data1 = 0xdeadbeef; | |
170 | reparse_buf->ReparseGuid.Data2 = 0xdead; | |
171 | reparse_buf->ReparseGuid.Data3 = 0xbeef; | |
172 | reparse_buf->ReparseGuid.Data4[0] = 42; | |
173 | reparse_buf->ReparseGuid.Data4[1] = 42; | |
174 | reparse_buf->ReparseGuid.Data4[2] = 42; | |
175 | reparse_buf->ReparseGuid.Data4[3] = 42; | |
176 | reparse_buf->ReparseGuid.Data4[4] = 42; | |
177 | reparse_buf->ReparseGuid.Data4[5] = 42; | |
178 | reparse_buf->ReparseGuid.Data4[6] = 42; | |
179 | reparse_buf->ReparseGuid.Data4[7] = 42; | |
180 | reparse_buf->ReparseGuid.Data4[8] = 42; | |
181 | ||
182 | memcpy(reparse_buf->GenericReparseBuffer.DataBuffer, | |
183 | reparse_data, strlen(reparse_data) + 1); | |
184 | ||
185 | handle = CreateFileA(path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, | |
186 | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); | |
187 | cl_win32_pass(handle != INVALID_HANDLE_VALUE); | |
188 | ||
189 | cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, | |
190 | reparse_buf, | |
191 | reparse_buf->ReparseDataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, | |
192 | NULL, 0, &ioctl_ret, NULL)); | |
193 | ||
194 | CloseHandle(handle); | |
195 | LocalFree(reparse_buf); | |
196 | } | |
197 | ||
198 | #endif | |
199 | ||
65477db1 ET |
200 | void test_core_link__stat_regular_file(void) |
201 | { | |
202 | struct stat st; | |
203 | ||
204 | cl_git_rewritefile("stat_regfile", "This is a regular file!\n"); | |
205 | ||
206 | cl_must_pass(p_stat("stat_regfile", &st)); | |
207 | cl_assert(S_ISREG(st.st_mode)); | |
208 | cl_assert_equal_i(24, st.st_size); | |
209 | } | |
210 | ||
211 | void test_core_link__lstat_regular_file(void) | |
212 | { | |
213 | struct stat st; | |
214 | ||
215 | cl_git_rewritefile("lstat_regfile", "This is a regular file!\n"); | |
216 | ||
217 | cl_must_pass(p_stat("lstat_regfile", &st)); | |
218 | cl_assert(S_ISREG(st.st_mode)); | |
219 | cl_assert_equal_i(24, st.st_size); | |
220 | } | |
221 | ||
222 | void test_core_link__stat_symlink(void) | |
223 | { | |
224 | struct stat st; | |
225 | ||
6b11eb51 ET |
226 | if (!should_run()) |
227 | clar__skip(); | |
228 | ||
65477db1 ET |
229 | cl_git_rewritefile("stat_target", "This is the target of a symbolic link.\n"); |
230 | do_symlink("stat_target", "stat_symlink", 0); | |
231 | ||
232 | cl_must_pass(p_stat("stat_target", &st)); | |
233 | cl_assert(S_ISREG(st.st_mode)); | |
234 | cl_assert_equal_i(39, st.st_size); | |
235 | ||
236 | cl_must_pass(p_stat("stat_symlink", &st)); | |
237 | cl_assert(S_ISREG(st.st_mode)); | |
238 | cl_assert_equal_i(39, st.st_size); | |
239 | } | |
240 | ||
241 | void test_core_link__stat_symlink_directory(void) | |
242 | { | |
243 | struct stat st; | |
244 | ||
6b11eb51 ET |
245 | if (!should_run()) |
246 | clar__skip(); | |
247 | ||
65477db1 ET |
248 | p_mkdir("stat_dirtarget", 0777); |
249 | do_symlink("stat_dirtarget", "stat_dirlink", 1); | |
250 | ||
251 | cl_must_pass(p_stat("stat_dirtarget", &st)); | |
252 | cl_assert(S_ISDIR(st.st_mode)); | |
253 | ||
254 | cl_must_pass(p_stat("stat_dirlink", &st)); | |
255 | cl_assert(S_ISDIR(st.st_mode)); | |
256 | } | |
257 | ||
258 | void test_core_link__stat_symlink_chain(void) | |
259 | { | |
260 | struct stat st; | |
261 | ||
6b11eb51 ET |
262 | if (!should_run()) |
263 | clar__skip(); | |
264 | ||
65477db1 ET |
265 | cl_git_rewritefile("stat_final_target", "Final target of some symbolic links...\n"); |
266 | do_symlink("stat_final_target", "stat_chain_3", 0); | |
267 | do_symlink("stat_chain_3", "stat_chain_2", 0); | |
268 | do_symlink("stat_chain_2", "stat_chain_1", 0); | |
269 | ||
270 | cl_must_pass(p_stat("stat_chain_1", &st)); | |
271 | cl_assert(S_ISREG(st.st_mode)); | |
272 | cl_assert_equal_i(39, st.st_size); | |
273 | } | |
274 | ||
275 | void test_core_link__stat_dangling_symlink(void) | |
276 | { | |
277 | struct stat st; | |
278 | ||
6b11eb51 ET |
279 | if (!should_run()) |
280 | clar__skip(); | |
281 | ||
65477db1 ET |
282 | do_symlink("stat_nonexistent", "stat_dangling", 0); |
283 | ||
284 | cl_must_fail(p_stat("stat_nonexistent", &st)); | |
285 | cl_must_fail(p_stat("stat_dangling", &st)); | |
286 | } | |
287 | ||
288 | void test_core_link__stat_dangling_symlink_directory(void) | |
289 | { | |
290 | struct stat st; | |
291 | ||
6b11eb51 ET |
292 | if (!should_run()) |
293 | clar__skip(); | |
294 | ||
65477db1 ET |
295 | do_symlink("stat_nonexistent", "stat_dangling_dir", 1); |
296 | ||
297 | cl_must_fail(p_stat("stat_nonexistent_dir", &st)); | |
298 | cl_must_fail(p_stat("stat_dangling", &st)); | |
299 | } | |
300 | ||
301 | void test_core_link__lstat_symlink(void) | |
302 | { | |
303 | git_buf target_path = GIT_BUF_INIT; | |
304 | struct stat st; | |
305 | ||
6b11eb51 ET |
306 | if (!should_run()) |
307 | clar__skip(); | |
308 | ||
65477db1 ET |
309 | /* Windows always writes the canonical path as the link target, so |
310 | * write the full path on all platforms. | |
311 | */ | |
312 | git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_target"); | |
313 | ||
314 | cl_git_rewritefile("lstat_target", "This is the target of a symbolic link.\n"); | |
315 | do_symlink(git_buf_cstr(&target_path), "lstat_symlink", 0); | |
316 | ||
317 | cl_must_pass(p_lstat("lstat_target", &st)); | |
318 | cl_assert(S_ISREG(st.st_mode)); | |
319 | cl_assert_equal_i(39, st.st_size); | |
320 | ||
321 | cl_must_pass(p_lstat("lstat_symlink", &st)); | |
322 | cl_assert(S_ISLNK(st.st_mode)); | |
323 | cl_assert_equal_i(git_buf_len(&target_path), st.st_size); | |
324 | ||
ac3d33df | 325 | git_buf_dispose(&target_path); |
65477db1 ET |
326 | } |
327 | ||
328 | void test_core_link__lstat_symlink_directory(void) | |
329 | { | |
330 | git_buf target_path = GIT_BUF_INIT; | |
331 | struct stat st; | |
332 | ||
6b11eb51 ET |
333 | if (!should_run()) |
334 | clar__skip(); | |
335 | ||
65477db1 ET |
336 | git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_dirtarget"); |
337 | ||
338 | p_mkdir("lstat_dirtarget", 0777); | |
339 | do_symlink(git_buf_cstr(&target_path), "lstat_dirlink", 1); | |
340 | ||
341 | cl_must_pass(p_lstat("lstat_dirtarget", &st)); | |
342 | cl_assert(S_ISDIR(st.st_mode)); | |
343 | ||
344 | cl_must_pass(p_lstat("lstat_dirlink", &st)); | |
345 | cl_assert(S_ISLNK(st.st_mode)); | |
346 | cl_assert_equal_i(git_buf_len(&target_path), st.st_size); | |
347 | ||
ac3d33df | 348 | git_buf_dispose(&target_path); |
65477db1 ET |
349 | } |
350 | ||
351 | void test_core_link__lstat_dangling_symlink(void) | |
352 | { | |
353 | struct stat st; | |
354 | ||
6b11eb51 ET |
355 | if (!should_run()) |
356 | clar__skip(); | |
357 | ||
65477db1 ET |
358 | do_symlink("lstat_nonexistent", "lstat_dangling", 0); |
359 | ||
360 | cl_must_fail(p_lstat("lstat_nonexistent", &st)); | |
361 | ||
362 | cl_must_pass(p_lstat("lstat_dangling", &st)); | |
363 | cl_assert(S_ISLNK(st.st_mode)); | |
364 | cl_assert_equal_i(strlen("lstat_nonexistent"), st.st_size); | |
365 | } | |
366 | ||
367 | void test_core_link__lstat_dangling_symlink_directory(void) | |
368 | { | |
369 | struct stat st; | |
370 | ||
6b11eb51 ET |
371 | if (!should_run()) |
372 | clar__skip(); | |
373 | ||
65477db1 ET |
374 | do_symlink("lstat_nonexistent", "lstat_dangling_dir", 1); |
375 | ||
376 | cl_must_fail(p_lstat("lstat_nonexistent", &st)); | |
377 | ||
378 | cl_must_pass(p_lstat("lstat_dangling_dir", &st)); | |
379 | cl_assert(S_ISLNK(st.st_mode)); | |
380 | cl_assert_equal_i(strlen("lstat_nonexistent"), st.st_size); | |
381 | } | |
382 | ||
383 | void test_core_link__stat_junction(void) | |
384 | { | |
385 | #ifdef GIT_WIN32 | |
386 | git_buf target_path = GIT_BUF_INIT; | |
387 | struct stat st; | |
388 | ||
389 | git_buf_join(&target_path, '/', clar_sandbox_path(), "stat_junctarget"); | |
390 | ||
391 | p_mkdir("stat_junctarget", 0777); | |
392 | do_junction(git_buf_cstr(&target_path), "stat_junction"); | |
393 | ||
394 | cl_must_pass(p_stat("stat_junctarget", &st)); | |
395 | cl_assert(S_ISDIR(st.st_mode)); | |
396 | ||
397 | cl_must_pass(p_stat("stat_junction", &st)); | |
398 | cl_assert(S_ISDIR(st.st_mode)); | |
399 | ||
ac3d33df | 400 | git_buf_dispose(&target_path); |
65477db1 ET |
401 | #endif |
402 | } | |
403 | ||
404 | void test_core_link__stat_dangling_junction(void) | |
405 | { | |
406 | #ifdef GIT_WIN32 | |
407 | git_buf target_path = GIT_BUF_INIT; | |
408 | struct stat st; | |
409 | ||
410 | git_buf_join(&target_path, '/', clar_sandbox_path(), "stat_nonexistent_junctarget"); | |
411 | ||
412 | p_mkdir("stat_nonexistent_junctarget", 0777); | |
413 | do_junction(git_buf_cstr(&target_path), "stat_dangling_junction"); | |
414 | ||
415 | RemoveDirectory("stat_nonexistent_junctarget"); | |
416 | ||
417 | cl_must_fail(p_stat("stat_nonexistent_junctarget", &st)); | |
418 | cl_must_fail(p_stat("stat_dangling_junction", &st)); | |
419 | ||
ac3d33df | 420 | git_buf_dispose(&target_path); |
65477db1 ET |
421 | #endif |
422 | } | |
423 | ||
424 | void test_core_link__lstat_junction(void) | |
425 | { | |
426 | #ifdef GIT_WIN32 | |
427 | git_buf target_path = GIT_BUF_INIT; | |
428 | struct stat st; | |
429 | ||
430 | git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_junctarget"); | |
431 | ||
432 | p_mkdir("lstat_junctarget", 0777); | |
433 | do_junction(git_buf_cstr(&target_path), "lstat_junction"); | |
434 | ||
435 | cl_must_pass(p_lstat("lstat_junctarget", &st)); | |
436 | cl_assert(S_ISDIR(st.st_mode)); | |
437 | ||
438 | cl_must_pass(p_lstat("lstat_junction", &st)); | |
439 | cl_assert(S_ISLNK(st.st_mode)); | |
440 | ||
ac3d33df | 441 | git_buf_dispose(&target_path); |
65477db1 ET |
442 | #endif |
443 | } | |
444 | ||
445 | void test_core_link__lstat_dangling_junction(void) | |
446 | { | |
447 | #ifdef GIT_WIN32 | |
448 | git_buf target_path = GIT_BUF_INIT; | |
449 | struct stat st; | |
450 | ||
451 | git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_nonexistent_junctarget"); | |
452 | ||
453 | p_mkdir("lstat_nonexistent_junctarget", 0777); | |
454 | do_junction(git_buf_cstr(&target_path), "lstat_dangling_junction"); | |
455 | ||
456 | RemoveDirectory("lstat_nonexistent_junctarget"); | |
457 | ||
458 | cl_must_fail(p_lstat("lstat_nonexistent_junctarget", &st)); | |
459 | ||
460 | cl_must_pass(p_lstat("lstat_dangling_junction", &st)); | |
461 | cl_assert(S_ISLNK(st.st_mode)); | |
462 | cl_assert_equal_i(git_buf_len(&target_path), st.st_size); | |
463 | ||
ac3d33df | 464 | git_buf_dispose(&target_path); |
65477db1 ET |
465 | #endif |
466 | } | |
467 | ||
468 | void test_core_link__stat_hardlink(void) | |
469 | { | |
470 | struct stat st; | |
471 | ||
6b11eb51 ET |
472 | if (!should_run()) |
473 | clar__skip(); | |
474 | ||
65477db1 ET |
475 | cl_git_rewritefile("stat_hardlink1", "This file has many names!\n"); |
476 | do_hardlink("stat_hardlink1", "stat_hardlink2"); | |
477 | ||
478 | cl_must_pass(p_stat("stat_hardlink1", &st)); | |
479 | cl_assert(S_ISREG(st.st_mode)); | |
480 | cl_assert_equal_i(26, st.st_size); | |
481 | ||
482 | cl_must_pass(p_stat("stat_hardlink2", &st)); | |
483 | cl_assert(S_ISREG(st.st_mode)); | |
484 | cl_assert_equal_i(26, st.st_size); | |
485 | } | |
486 | ||
487 | void test_core_link__lstat_hardlink(void) | |
488 | { | |
489 | struct stat st; | |
490 | ||
6b11eb51 ET |
491 | if (!should_run()) |
492 | clar__skip(); | |
493 | ||
65477db1 ET |
494 | cl_git_rewritefile("lstat_hardlink1", "This file has many names!\n"); |
495 | do_hardlink("lstat_hardlink1", "lstat_hardlink2"); | |
496 | ||
497 | cl_must_pass(p_lstat("lstat_hardlink1", &st)); | |
498 | cl_assert(S_ISREG(st.st_mode)); | |
499 | cl_assert_equal_i(26, st.st_size); | |
500 | ||
501 | cl_must_pass(p_lstat("lstat_hardlink2", &st)); | |
502 | cl_assert(S_ISREG(st.st_mode)); | |
503 | cl_assert_equal_i(26, st.st_size); | |
504 | } | |
505 | ||
506 | void test_core_link__stat_reparse_point(void) | |
507 | { | |
508 | #ifdef GIT_WIN32 | |
509 | struct stat st; | |
510 | ||
511 | /* Generic reparse points should be treated as regular files, only | |
512 | * symlinks and junctions should be treated as links. | |
513 | */ | |
514 | ||
515 | cl_git_rewritefile("stat_reparse", "This is a reparse point!\n"); | |
516 | do_custom_reparse("stat_reparse"); | |
517 | ||
518 | cl_must_pass(p_lstat("stat_reparse", &st)); | |
519 | cl_assert(S_ISREG(st.st_mode)); | |
520 | cl_assert_equal_i(25, st.st_size); | |
521 | #endif | |
522 | } | |
523 | ||
524 | void test_core_link__lstat_reparse_point(void) | |
525 | { | |
526 | #ifdef GIT_WIN32 | |
527 | struct stat st; | |
528 | ||
529 | cl_git_rewritefile("lstat_reparse", "This is a reparse point!\n"); | |
530 | do_custom_reparse("lstat_reparse"); | |
531 | ||
532 | cl_must_pass(p_lstat("lstat_reparse", &st)); | |
533 | cl_assert(S_ISREG(st.st_mode)); | |
534 | cl_assert_equal_i(25, st.st_size); | |
535 | #endif | |
536 | } | |
537 | ||
538 | void test_core_link__readlink_nonexistent_file(void) | |
539 | { | |
540 | char buf[2048]; | |
541 | ||
542 | cl_must_fail(p_readlink("readlink_nonexistent", buf, 2048)); | |
543 | cl_assert_equal_i(ENOENT, errno); | |
544 | } | |
545 | ||
546 | void test_core_link__readlink_normal_file(void) | |
547 | { | |
548 | char buf[2048]; | |
549 | ||
550 | cl_git_rewritefile("readlink_regfile", "This is a regular file!\n"); | |
551 | cl_must_fail(p_readlink("readlink_regfile", buf, 2048)); | |
552 | cl_assert_equal_i(EINVAL, errno); | |
553 | } | |
554 | ||
555 | void test_core_link__readlink_symlink(void) | |
556 | { | |
557 | git_buf target_path = GIT_BUF_INIT; | |
558 | int len; | |
559 | char buf[2048]; | |
560 | ||
6b11eb51 ET |
561 | if (!should_run()) |
562 | clar__skip(); | |
563 | ||
65477db1 ET |
564 | git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_target"); |
565 | ||
566 | cl_git_rewritefile("readlink_target", "This is the target of a symlink\n"); | |
567 | do_symlink(git_buf_cstr(&target_path), "readlink_link", 0); | |
568 | ||
569 | len = p_readlink("readlink_link", buf, 2048); | |
570 | cl_must_pass(len); | |
571 | ||
572 | buf[len] = 0; | |
573 | ||
cceae9a2 | 574 | cl_assert_equal_s(git_buf_cstr(&target_path), buf); |
65477db1 | 575 | |
ac3d33df | 576 | git_buf_dispose(&target_path); |
65477db1 ET |
577 | } |
578 | ||
579 | void test_core_link__readlink_dangling(void) | |
580 | { | |
581 | git_buf target_path = GIT_BUF_INIT; | |
582 | int len; | |
583 | char buf[2048]; | |
584 | ||
6b11eb51 ET |
585 | if (!should_run()) |
586 | clar__skip(); | |
587 | ||
65477db1 ET |
588 | git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_nonexistent"); |
589 | ||
590 | do_symlink(git_buf_cstr(&target_path), "readlink_dangling", 0); | |
591 | ||
592 | len = p_readlink("readlink_dangling", buf, 2048); | |
593 | cl_must_pass(len); | |
594 | ||
595 | buf[len] = 0; | |
596 | ||
cceae9a2 | 597 | cl_assert_equal_s(git_buf_cstr(&target_path), buf); |
65477db1 | 598 | |
ac3d33df | 599 | git_buf_dispose(&target_path); |
65477db1 ET |
600 | } |
601 | ||
602 | void test_core_link__readlink_multiple(void) | |
603 | { | |
604 | git_buf target_path = GIT_BUF_INIT, | |
605 | path3 = GIT_BUF_INIT, path2 = GIT_BUF_INIT, path1 = GIT_BUF_INIT; | |
606 | int len; | |
607 | char buf[2048]; | |
608 | ||
6b11eb51 ET |
609 | if (!should_run()) |
610 | clar__skip(); | |
611 | ||
65477db1 ET |
612 | git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_final"); |
613 | git_buf_join(&path3, '/', clar_sandbox_path(), "readlink_3"); | |
614 | git_buf_join(&path2, '/', clar_sandbox_path(), "readlink_2"); | |
615 | git_buf_join(&path1, '/', clar_sandbox_path(), "readlink_1"); | |
616 | ||
617 | do_symlink(git_buf_cstr(&target_path), git_buf_cstr(&path3), 0); | |
618 | do_symlink(git_buf_cstr(&path3), git_buf_cstr(&path2), 0); | |
619 | do_symlink(git_buf_cstr(&path2), git_buf_cstr(&path1), 0); | |
620 | ||
621 | len = p_readlink("readlink_1", buf, 2048); | |
622 | cl_must_pass(len); | |
623 | ||
624 | buf[len] = 0; | |
625 | ||
cceae9a2 | 626 | cl_assert_equal_s(git_buf_cstr(&path2), buf); |
65477db1 | 627 | |
ac3d33df JK |
628 | git_buf_dispose(&path1); |
629 | git_buf_dispose(&path2); | |
630 | git_buf_dispose(&path3); | |
631 | git_buf_dispose(&target_path); | |
65477db1 | 632 | } |