Line Flow Count Block(s) Source
1 - /*
2 - * Copyright (C) the libgit2 contributors. All rights reserved.
3 - *
4 - * This file is part of libgit2, distributed under the GNU GPL v2 with
5 - * a Linking Exception. For full terms see the included COPYING file.
6 - */
7 -
8 - #include "path.h"
9 -
10 - #include "posix.h"
11 - #include "repository.h"
12 - #ifdef GIT_WIN32
13 - #include "win32/posix.h"
14 - #include "win32/w32_buffer.h"
15 - #include "win32/w32_util.h"
16 - #include "win32/version.h"
17 - #include <aclapi.h>
18 - #else
19 - #include <dirent.h>
20 - #endif
21 - #include <stdio.h>
22 - #include <ctype.h>
23 -
24 657289 2 static int dos_drive_prefix_length(const char *path)
25 - {
26 - int i;
27 -
28 - /*
29 - * Does it start with an ASCII letter (i.e. highest bit not set),
30 - * followed by a colon?
31 - */
32 657289 2 if (!(0x80 & (unsigned char)*path))
33 656962 3-7 return *path && path[1] == ':' ? 2 : 0;
34 -
35 - /*
36 - * While drive letters must be letters of the English alphabet, it is
37 - * possible to assign virtually _any_ Unicode character via `subst` as
38 - * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
39 - * like this:
40 - *
41 - * subst ֍: %USERPROFILE%\Desktop
42 - */
43 993 8-11 for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
44 - ; /* skip first UTF-8 character */
45 327 12 return path[i] == ':' ? i + 1 : 0;
46 - }
47 -
48 - #ifdef GIT_WIN32
49 - static bool looks_like_network_computer_name(const char *path, int pos)
50 - {
51 - if (pos < 3)
52 - return false;
53 -
54 - if (path[0] != '/' || path[1] != '/')
55 - return false;
56 -
57 - while (pos-- > 2) {
58 - if (path[pos] == '/')
59 - return false;
60 - }
61 -
62 - return true;
63 - }
64 - #endif
65 -
66 - /*
67 - * Based on the Android implementation, BSD licensed.
68 - * http://android.git.kernel.org/
69 - *
70 - * Copyright (C) 2008 The Android Open Source Project
71 - * All rights reserved.
72 - *
73 - * Redistribution and use in source and binary forms, with or without
74 - * modification, are permitted provided that the following conditions
75 - * are met:
76 - * * Redistributions of source code must retain the above copyright
77 - * notice, this list of conditions and the following disclaimer.
78 - * * Redistributions in binary form must reproduce the above copyright
79 - * notice, this list of conditions and the following disclaimer in
80 - * the documentation and/or other materials provided with the
81 - * distribution.
82 - *
83 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
84 - * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
85 - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
86 - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
87 - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
88 - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
89 - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
90 - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
91 - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
92 - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
93 - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
94 - * SUCH DAMAGE.
95 - */
96 8007 2 int git_path_basename_r(git_buf *buffer, const char *path)
97 - {
98 - const char *endp, *startp;
99 - int len, result;
100 -
101 - /* Empty or NULL string gets treated as "." */
102 8007 2,3 if (path == NULL || *path == '\0') {
103 4 4 startp = ".";
104 4 4 len = 1;
105 4 4 goto Exit;
106 - }
107 -
108 - /* Strip trailing slashes */
109 8003 5 endp = path + strlen(path) - 1;
110 8012 5,7,8 while (endp > path && *endp == '/')
111 9 6 endp--;
112 -
113 - /* All slashes becomes "/" */
114 8003 9,10 if (endp == path && *endp == '/') {
115 2 11 startp = "/";
116 2 11 len = 1;
117 2 11 goto Exit;
118 - }
119 -
120 - /* Find the start of the base */
121 8001 12 startp = endp;
122 109027 12,14,15 while (startp > path && *(startp - 1) != '/')
123 101026 13 startp--;
124 -
125 - /* Cast is safe because max path < max int */
126 8001 16 len = (int)(endp - startp + 1);
127 -
128 - Exit:
129 8007 17 result = len;
130 -
131 8007 17-19 if (buffer != NULL && git_buf_set(buffer, startp, len) < 0)
132 ##### 20 return -1;
133 -
134 8007 21 return result;
135 - }
136 -
137 - /*
138 - * Determine if the path is a Windows prefix and, if so, returns
139 - * its actual lentgh. If it is not a prefix, returns -1.
140 - */
141 164851 2 static int win32_prefix_length(const char *path, int len)
142 - {
143 - #ifndef GIT_WIN32
144 - GIT_UNUSED(path);
145 - GIT_UNUSED(len);
146 - #else
147 - /*
148 - * Mimic unix behavior where '/.git' returns '/': 'C:/.git'
149 - * will return 'C:/' here
150 - */
151 - if (dos_drive_prefix_length(path) == len)
152 - return len;
153 -
154 - /*
155 - * Similarly checks if we're dealing with a network computer name
156 - * '//computername/.git' will return '//computername/'
157 - */
158 - if (looks_like_network_computer_name(path, len))
159 - return len;
160 - #endif
161 -
162 164851 2 return -1;
163 - }
164 -
165 - /*
166 - * Based on the Android implementation, BSD licensed.
167 - * Check http://android.git.kernel.org/
168 - */
169 - 2 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files)int git_path_dirname_r(git_buf *buffer, const char *path)
170 - {
171 - const char *endp;
172 - 2 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) int is_prefix = 0, len;
173 -
174 - /* Empty or NULL string gets treated as "." */
175 - 2,3 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (path == NULL || *path == '\0') {
176 - 4 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) path = ".";
177 - 4 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = 1;
178 - 4 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit;
179 - }
180 -
181 - /* Strip trailing slashes */
182 - 5 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) endp = path + strlen(path) - 1;
183 - 5,7,8 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) while (endp > path && *endp == '/')
184 - 6 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) endp--;
185 -
186 - 9 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (endp - path + 1 > INT_MAX) {
187 - 10 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) git_error_set(GIT_ERROR_INVALID, "path too long");
188 - 11 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = -1;
189 - 11 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit;
190 - }
191 -
192 - 12,13 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
193 - 14 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) is_prefix = 1;
194 - 14 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit;
195 - }
196 -
197 - /* Find the start of the dir */
198 - 15,17,18 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) while (endp > path && *endp != '/')
199 - 16 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) endp--;
200 -
201 - /* Either the dir is "/" or there are no slashes */
202 - 19 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (endp == path) {
203 - 20-22 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) path = (*endp == '/') ? "/" : ".";
204 - 23 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = 1;
205 - 23 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit;
206 - }
207 -
208 - do {
209 - 24 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) endp--;
210 - 24,25 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) } while (endp > path && *endp == '/');
211 -
212 - 26 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (endp - path + 1 > INT_MAX) {
213 - 27 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) git_error_set(GIT_ERROR_INVALID, "path too long");
214 - 28 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = -1;
215 - 28 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit;
216 - }
217 -
218 - 29,30 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
219 - 31 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) is_prefix = 1;
220 - 31 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) goto Exit;
221 - }
222 -
223 - /* Cast is safe because max path < max int */
224 - 32 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) len = (int)(endp - path + 1);
225 -
226 - Exit:
227 - 33 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (buffer) {
228 - 34,35 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (git_buf_set(buffer, path, len) < 0)
229 - 36 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) return -1;
230 - 37-39 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) if (is_prefix && git_buf_putc(buffer, '/') < 0)
231 - 40 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) return -1;
232 - }
233 -
234 - 41 suppressed: function cannot be solved git_path_dirname_r (automatic due to inconsistent arc counts in .gcda files) return len;
235 - }
236 -
237 -
238 101 2 char *git_path_dirname(const char *path)
239 - {
240 101 2 git_buf buf = GIT_BUF_INIT;
241 - char *dirname;
242 -
243 101 2 git_path_dirname_r(&buf, path);
244 101 3 dirname = git_buf_detach(&buf);
245 101 4 git_buf_dispose(&buf); /* avoid memleak if error occurs */
246 -
247 101 5 return dirname;
248 - }
249 -
250 7996 2 char *git_path_basename(const char *path)
251 - {
252 7996 2 git_buf buf = GIT_BUF_INIT;
253 - char *basename;
254 -
255 7996 2 git_path_basename_r(&buf, path);
256 7996 3 basename = git_buf_detach(&buf);
257 7996 4 git_buf_dispose(&buf); /* avoid memleak if error occurs */
258 -
259 7996 5 return basename;
260 - }
261 -
262 2138 2 size_t git_path_basename_offset(git_buf *buffer)
263 - {
264 - ssize_t slash;
265 -
266 2138 2,3 if (!buffer || buffer->size <= 0)
267 ##### 4 return 0;
268 -
269 2138 5 slash = git_buf_rfind_next(buffer, '/');
270 -
271 2138 6,7 if (slash >= 0 && buffer->ptr[slash] == '/')
272 2136 8 return (size_t)(slash + 1);
273 -
274 2 9 return 0;
275 - }
276 -
277 11 2 const char *git_path_topdir(const char *path)
278 - {
279 - size_t len;
280 - ssize_t i;
281 -
282 11 2,3 assert(path);
283 11 4 len = strlen(path);
284 -
285 11 4,5 if (!len || path[len - 1] != '/')
286 4 6 return NULL;
287 -
288 28 7,10,11 for (i = (ssize_t)len - 2; i >= 0; --i)
289 25 8 if (path[i] == '/')
290 4 9 break;
291 -
292 7 12 return &path[i + 1];
293 - }
294 -
295 657166 2 int git_path_root(const char *path)
296 - {
297 657166 2 int offset = 0, prefix_len;
298 -
299 - /* Does the root of the path look like a windows drive ? */
300 657166 2,3 if ((prefix_len = dos_drive_prefix_length(path)))
301 12 4 offset += prefix_len;
302 -
303 - #ifdef GIT_WIN32
304 - /* Are we dealing with a windows network path? */
305 - else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') ||
306 - (path[0] == '\\' && path[1] == '\\' && path[2] != '\\'))
307 - {
308 - offset += 2;
309 -
310 - /* Skip the computer name segment */
311 - while (path[offset] && path[offset] != '/' && path[offset] != '\\')
312 - offset++;
313 - }
314 -
315 - if (path[offset] == '\\')
316 - return offset;
317 - #endif
318 -
319 657154 5 if (path[offset] == '/')
320 60610 6 return offset;
321 -
322 596544 7 return -1; /* Not a real error - signals that path is not rooted */
323 - }
324 -
325 6748 2 static void path_trim_slashes(git_buf *path)
326 - {
327 6748 2 int ceiling = git_path_root(path->ptr) + 1;
328 6748 3,4 assert(ceiling >= 0);
329 -
330 13261 5,9 while (path->size > (size_t)ceiling) {
331 13260 6 if (path->ptr[path->size-1] != '/')
332 6747 7 break;
333 -
334 6513 8 path->ptr[path->size-1] = '\0';
335 6513 8 path->size--;
336 - }
337 6748 10 }
338 -
339 - 2 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files)int git_path_join_unrooted(
340 - git_buf *path_out, const char *path, const char *base, ssize_t *root_at)
341 - {
342 - ssize_t root;
343 -
344 - 2-4 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) assert(path && path_out);
345 -
346 - 5 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) root = (ssize_t)git_path_root(path);
347 -
348 - 6,7 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (base != NULL && root < 0) {
349 - 8,9 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (git_buf_joinpath(path_out, base, path) < 0)
350 - 10 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) return -1;
351 -
352 - 11 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) root = (ssize_t)strlen(base);
353 - } else {
354 - 12,13 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (git_buf_sets(path_out, path) < 0)
355 - 14 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) return -1;
356 -
357 - 15 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (root < 0)
358 - 16 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) root = 0;
359 - 17 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) else if (base)
360 - 18 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) git_path_equal_or_prefixed(base, path, &root);
361 - }
362 -
363 - 19 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) if (root_at)
364 - 20 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) *root_at = root;
365 -
366 - 21 suppressed: function cannot be solved git_path_join_unrooted (automatic due to inconsistent arc counts in .gcda files) return 0;
367 - }
368 -
369 1139 2 void git_path_squash_slashes(git_buf *path)
370 - {
371 - char *p, *q;
372 -
373 1139 2 if (path->size == 0)
374 1139 3,12 return;
375 -
376 15617 4,9,10 for (p = path->ptr, q = path->ptr; *q; p++, q++) {
377 14483 5 *p = *q;
378 -
379 14483 5,7,8 while (*q == '/' && *(q+1) == '/') {
380 ##### 6 path->size--;
381 ##### 6 q++;
382 - }
383 - }
384 -
385 1134 11 *p = '\0';
386 - }
387 -
388 - 2 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files)int git_path_prettify(git_buf *path_out, const char *path, const char *base)
389 - {
390 - char buf[GIT_PATH_MAX];
391 -
392 - 2-4 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) assert(path && path_out);
393 -
394 - /* construct path if needed */
395 - 5-7 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) if (base != NULL && git_path_root(path) < 0) {
396 - 8,9 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) if (git_buf_joinpath(path_out, base, path) < 0)
397 - 10 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) return -1;
398 - 11 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) path = path_out->ptr;
399 - }
400 -
401 - 12,13 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) if (p_realpath(path, buf) == NULL) {
402 - /* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */
403 - 14-19 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;
404 - 20 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) git_error_set(GIT_ERROR_OS, "failed to resolve path '%s'", path);
405 -
406 - 21 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) git_buf_clear(path_out);
407 -
408 - 22 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) return error;
409 - }
410 -
411 - 23 suppressed: function cannot be solved git_path_prettify (automatic due to inconsistent arc counts in .gcda files) return git_buf_sets(path_out, buf);
412 - }
413 -
414 2190 2 int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)
415 - {
416 2190 2 int error = git_path_prettify(path_out, path, base);
417 2190 3 return (error < 0) ? error : git_path_to_dir(path_out);
418 - }
419 -
420 - 2 suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files)int git_path_to_dir(git_buf *path)
421 - {
422 - 2,4 suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) if (path->asize > 0 &&
423 - 3,6 suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) git_buf_len(path) > 0 &&
424 - 5 suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) path->ptr[git_buf_len(path) - 1] != '/')
425 - 7 suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) git_buf_putc(path, '/');
426 -
427 - 8 suppressed: function cannot be solved git_path_to_dir (automatic due to inconsistent arc counts in .gcda files) return git_buf_oom(path) ? -1 : 0;
428 - }
429 -
430 8 2 void git_path_string_to_dir(char* path, size_t size)
431 - {
432 8 2 size_t end = strlen(path);
433 -
434 8 2-4 if (end && path[end - 1] != '/' && end < size) {
435 4 5 path[end] = '/';
436 4 5 path[end + 1] = '\0';
437 - }
438 8 6 }
439 -
440 97 2 int git__percent_decode(git_buf *decoded_out, const char *input)
441 - {
442 - int len, hi, lo, i;
443 97 2-4 assert(decoded_out && input);
444 -
445 97 5 len = (int)strlen(input);
446 97 5 git_buf_clear(decoded_out);
447 -
448 4758 6,19,20 for(i = 0; i < len; i++)
449 - {
450 4661 7 char c = input[i];
451 -
452 4661 7 if (c != '%')
453 4633 8 goto append;
454 -
455 28 9 if (i >= len - 2)
456 3 10 goto append;
457 -
458 25 11 hi = git__fromhex(input[i + 1]);
459 25 12 lo = git__fromhex(input[i + 2]);
460 -
461 25 13,14 if (hi < 0 || lo < 0)
462 - goto append;
463 -
464 22 15 c = (char)(hi << 4 | lo);
465 22 15 i += 2;
466 -
467 - append:
468 4661 16,17 if (git_buf_putc(decoded_out, c) < 0)
469 ##### 18 return -1;
470 - }
471 -
472 97 21 return 0;
473 - }
474 -
475 7 2 static int error_invalid_local_file_uri(const char *uri)
476 - {
477 7 2 git_error_set(GIT_ERROR_CONFIG, "'%s' is not a valid local file URI", uri);
478 7 3 return -1;
479 - }
480 -
481 333 2 static int local_file_url_prefixlen(const char *file_url)
482 - {
483 333 2 int len = -1;
484 -
485 333 2,3 if (git__prefixcmp(file_url, "file://") == 0) {
486 178 4 if (file_url[7] == '/')
487 166 5 len = 8;
488 12 6,7 else if (git__prefixcmp(file_url + 7, "localhost/") == 0)
489 7 8 len = 17;
490 - }
491 -
492 333 9 return len;
493 - }
494 -
495 239 2 bool git_path_is_local_file_url(const char *file_url)
496 - {
497 239 2 return (local_file_url_prefixlen(file_url) > 0);
498 - }
499 -
500 94 2 int git_path_fromurl(git_buf *local_path_out, const char *file_url)
501 - {
502 - int offset;
503 -
504 94 2-4 assert(local_path_out && file_url);
505 -
506 94 5-7 if ((offset = local_file_url_prefixlen(file_url)) < 0 ||
507 90 7,8 file_url[offset] == '\0' || file_url[offset] == '/')
508 7 9 return error_invalid_local_file_uri(file_url);
509 -
510 - #ifndef GIT_WIN32
511 87 10 offset--; /* A *nix absolute path starts with a forward slash */
512 - #endif
513 -
514 87 10 git_buf_clear(local_path_out);
515 87 11 return git__percent_decode(local_path_out, file_url + offset);
516 - }
517 -
518 - 2 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files)int git_path_walk_up(
519 - git_buf *path,
520 - const char *ceiling,
521 - int (*cb)(void *data, const char *),
522 - void *data)
523 - {
524 - 2 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) int error = 0;
525 - git_buf iter;
526 - 2 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) ssize_t stop = 0, scan;
527 - 2 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) char oldc = '\0';
528 -
529 - 2-4 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) assert(path && cb);
530 -
531 - 5 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (ceiling != NULL) {
532 - 6,7 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (git__prefixcmp(path->ptr, ceiling) == 0)
533 - 8 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) stop = (ssize_t)strlen(ceiling);
534 - else
535 - 9,10 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) stop = git_buf_len(path);
536 - }
537 - 11 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) scan = git_buf_len(path);
538 -
539 - /* empty path: yield only once */
540 - 12 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (!scan) {
541 - 13 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) error = cb(data, "");
542 - 14 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (error)
543 - 15 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) git_error_set_after_callback(error);
544 - 16 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) return error;
545 - }
546 -
547 - 17 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.ptr = path->ptr;
548 - 17 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.size = git_buf_len(path);
549 - 18 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.asize = path->asize;
550 -
551 - 18,26 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) while (scan >= stop) {
552 - 19 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) error = cb(data, iter.ptr);
553 - 20 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.ptr[scan] = oldc;
554 -
555 - 20 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (error) {
556 - 21 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) git_error_set_after_callback(error);
557 - 22 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) break;
558 - }
559 -
560 - 23 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) scan = git_buf_rfind_next(&iter, '/');
561 - 24 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (scan >= 0) {
562 - 25 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) scan++;
563 - 25 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) oldc = iter.ptr[scan];
564 - 25 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.size = scan;
565 - 25 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.ptr[scan] = '\0';
566 - }
567 - }
568 -
569 - 27 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (scan >= 0)
570 - 28 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) iter.ptr[scan] = oldc;
571 -
572 - /* relative path: yield for the last component */
573 - 29-31 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (!error && stop == 0 && iter.ptr[0] != '/') {
574 - 32 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) error = cb(data, "");
575 - 33 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) if (error)
576 - 34 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) git_error_set_after_callback(error);
577 - }
578 -
579 - 35 suppressed: function cannot be solved git_path_walk_up (automatic due to inconsistent arc counts in .gcda files) return error;
580 - }
581 -
582 219430 2 bool git_path_exists(const char *path)
583 - {
584 219430 2,3 assert(path);
585 219430 4 return p_access(path, F_OK) == 0;
586 - }
587 -
588 94699 2 bool git_path_isdir(const char *path)
589 - {
590 - struct stat st;
591 94719 2,3 if (p_stat(path, &st) < 0)
592 10005 4 return false;
593 -
594 84714 5 return S_ISDIR(st.st_mode) != 0;
595 - }
596 -
597 20881 2 bool git_path_isfile(const char *path)
598 - {
599 - struct stat st;
600 -
601 20881 2,3 assert(path);
602 20883 4,5 if (p_stat(path, &st) < 0)
603 10040 6 return false;
604 -
605 10843 7 return S_ISREG(st.st_mode) != 0;
606 - }
607 -
608 4 2 bool git_path_islink(const char *path)
609 - {
610 - struct stat st;
611 -
612 4 2,3 assert(path);
613 4 4,5 if (p_lstat(path, &st) < 0)
614 ##### 6 return false;
615 -
616 4 7 return S_ISLNK(st.st_mode) != 0;
617 - }
618 -
619 - #ifdef GIT_WIN32
620 -
621 - bool git_path_is_empty_dir(const char *path)
622 - {
623 - git_win32_path filter_w;
624 - bool empty = false;
625 -
626 - if (git_win32__findfirstfile_filter(filter_w, path)) {
627 - WIN32_FIND_DATAW findData;
628 - HANDLE hFind = FindFirstFileW(filter_w, &findData);
629 -
630 - /* FindFirstFile will fail if there are no children to the given
631 - * path, which can happen if the given path is a file (and obviously
632 - * has no children) or if the given path is an empty mount point.
633 - * (Most directories have at least directory entries '.' and '..',
634 - * but ridiculously another volume mounted in another drive letter's
635 - * path space do not, and thus have nothing to enumerate.) If
636 - * FindFirstFile fails, check if this is a directory-like thing
637 - * (a mount point).
638 - */
639 - if (hFind == INVALID_HANDLE_VALUE)
640 - return git_path_isdir(path);
641 -
642 - /* If the find handle was created successfully, then it's a directory */
643 - empty = true;
644 -
645 - do {
646 - /* Allow the enumeration to return . and .. and still be considered
647 - * empty. In the special case of drive roots (i.e. C:\) where . and
648 - * .. do not occur, we can still consider the path to be an empty
649 - * directory if there's nothing there. */
650 - if (!git_path_is_dot_or_dotdotW(findData.cFileName)) {
651 - empty = false;
652 - break;
653 - }
654 - } while (FindNextFileW(hFind, &findData));
655 -
656 - FindClose(hFind);
657 - }
658 -
659 - return empty;
660 - }
661 -
662 - #else
663 -
664 59 2 static int path_found_entry(void *payload, git_buf *path)
665 - {
666 - GIT_UNUSED(payload);
667 59 2 return !git_path_is_dot_or_dotdot(path->ptr);
668 - }
669 -
670 75 2 bool git_path_is_empty_dir(const char *path)
671 - {
672 - int error;
673 75 2 git_buf dir = GIT_BUF_INIT;
674 -
675 75 2,3 if (!git_path_isdir(path))
676 2 4 return false;
677 -
678 73 5,6 if ((error = git_buf_sets(&dir, path)) != 0)
679 ##### 7 git_error_clear();
680 - else
681 73 8 error = git_path_direach(&dir, 0, path_found_entry, NULL);
682 -
683 73 9 git_buf_dispose(&dir);
684 -
685 73 10 return !error;
686 - }
687 -
688 - #endif
689 -
690 45859 2 int git_path_set_error(int errno_value, const char *path, const char *action)
691 - {
692 45859 2 switch (errno_value) {
693 - case ENOENT:
694 - case ENOTDIR:
695 45856 3 git_error_set(GIT_ERROR_OS, "could not find '%s' to %s", path, action);
696 45853 4 return GIT_ENOTFOUND;
697 -
698 - case EINVAL:
699 - case ENAMETOOLONG:
700 ##### 5 git_error_set(GIT_ERROR_OS, "invalid path for filesystem '%s'", path);
701 ##### 6 return GIT_EINVALIDSPEC;
702 -
703 - case EEXIST:
704 ##### 7 git_error_set(GIT_ERROR_OS, "failed %s - '%s' already exists", action, path);
705 ##### 8 return GIT_EEXISTS;
706 -
707 - case EACCES:
708 3 9 git_error_set(GIT_ERROR_OS, "failed %s - '%s' is locked", action, path);
709 3 10 return GIT_ELOCKED;
710 -
711 - default:
712 ##### 11 git_error_set(GIT_ERROR_OS, "could not %s '%s'", action, path);
713 ##### 12 return -1;
714 - }
715 - }
716 -
717 33592 2 int git_path_lstat(const char *path, struct stat *st)
718 - {
719 33592 2,3 if (p_lstat(path, st) == 0)
720 31434 4 return 0;
721 -
722 2155 5 return git_path_set_error(errno, path, "stat");
723 - }
724 -
725 19388 2 static bool _check_dir_contents(
726 - git_buf *dir,
727 - const char *sub,
728 - bool (*predicate)(const char *))
729 - {
730 - bool result;
731 19388 2 size_t dir_size = git_buf_len(dir);
732 19387 3 size_t sub_size = strlen(sub);
733 - size_t alloc_size;
734 -
735 - /* leave base valid even if we could not make space for subdir */
736 19387 3-5,7 if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) ||
737 19387 6,8,10 GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) ||
738 19385 9 git_buf_try_grow(dir, alloc_size, false) < 0)
739 ##### 11 return false;
740 -
741 - /* save excursion */
742 19387 12,13 if (git_buf_joinpath(dir, dir->ptr, sub) < 0)
743 ##### 14 return false;
744 -
745 19385 15 result = predicate(dir->ptr);
746 -
747 - /* restore path */
748 19389 16 git_buf_truncate(dir, dir_size);
749 19389 17 return result;
750 - }
751 -
752 1201 2 bool git_path_contains(git_buf *dir, const char *item)
753 - {
754 1201 2 return _check_dir_contents(dir, item, &git_path_exists);
755 - }
756 -
757 8369 2 bool git_path_contains_dir(git_buf *base, const char *subdir)
758 - {
759 8369 2 return _check_dir_contents(base, subdir, &git_path_isdir);
760 - }
761 -
762 9820 2 bool git_path_contains_file(git_buf *base, const char *file)
763 - {
764 9820 2 return _check_dir_contents(base, file, &git_path_isfile);
765 - }
766 -
767 - 2 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files)int git_path_find_dir(git_buf *dir, const char *path, const char *base)
768 - {
769 - 2 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) int error = git_path_join_unrooted(dir, path, base, NULL);
770 -
771 - 3 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) if (!error) {
772 - char buf[GIT_PATH_MAX];
773 - 4,5 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) if (p_realpath(dir->ptr, buf) != NULL)
774 - 6,7 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) error = git_buf_sets(dir, buf);
775 - }
776 -
777 - /* call dirname if this is not a directory */
778 - 8 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) if (!error) /* && git_path_isdir(dir->ptr) == false) */
779 - 9-13 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0;
780 -
781 - 14 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) if (!error)
782 - 15 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) error = git_path_to_dir(dir);
783 -
784 - 16 suppressed: function cannot be solved git_path_find_dir (automatic due to inconsistent arc counts in .gcda files) return error;
785 - }
786 -
787 3152 2 int git_path_resolve_relative(git_buf *path, size_t ceiling)
788 - {
789 - char *base, *to, *from, *next;
790 - size_t len;
791 -
792 3152 2-5 GIT_ERROR_CHECK_ALLOC_BUF(path);
793 -
794 3152 6 if (ceiling > path->size)
795 ##### 7 ceiling = path->size;
796 -
797 - /* recognize drive prefixes, etc. that should not be backed over */
798 3152 8 if (ceiling == 0)
799 3152 9,10 ceiling = git_path_root(path->ptr) + 1;
800 -
801 - /* recognize URL prefixes that should not be backed over */
802 3152 11 if (ceiling == 0) {
803 3869 12-16 for (next = path->ptr; *next && git__isalpha(*next); ++next);
804 2973 17-19 if (next[0] == ':' && next[1] == '/' && next[2] == '/')
805 8 20 ceiling = (next + 3) - path->ptr;
806 - }
807 -
808 3152 21 base = to = from = path->ptr + ceiling;
809 -
810 8135 21,60 while (*from) {
811 22863 22-25 for (next = from; *next && *next != '/'; ++next);
812 -
813 4990 26 len = next - from;
814 -
815 4990 26-28 if (len == 1 && from[0] == '.')
816 - /* do nothing with singleton dot */;
817 -
818 2233 29-31 else if (len == 2 && from[0] == '.' && from[1] == '.') {
819 - /* error out if trying to up one from a hard base */
820 590 32,33 if (to == base && ceiling != 0) {
821 7 34 git_error_set(GIT_ERROR_INVALID,
822 - "cannot strip root component off url");
823 7 35 return -1;
824 - }
825 -
826 - /* no more path segments to strip,
827 - * use '../' as a new base path */
828 583 36,50 if (to == base) {
829 19 37 if (*next == '/')
830 16 38 len++;
831 -
832 19 39 if (to != from)
833 3 40 memmove(to, from, len);
834 -
835 19 41 to += len;
836 - /* this is now the base, can't back up from a
837 - * relative prefix */
838 19 41 base = to;
839 - } else {
840 - /* back up a path segment */
841 1128 42-45 while (to > base && to[-1] == '/') to--;
842 5966 46-49 while (to > base && to[-1] != '/') to--;
843 - }
844 - } else {
845 1643 51,52 if (*next == '/' && *from != '/')
846 1283 53 len++;
847 -
848 1643 54 if (to != from)
849 369 55 memmove(to, from, len);
850 -
851 1643 56 to += len;
852 - }
853 -
854 4983 57 from += len;
855 -
856 5594 57-59 while (*from == '/') from++;
857 - }
858 -
859 3145 61 *to = '\0';
860 -
861 3145 61 path->size = to - path->ptr;
862 -
863 3145 61 return 0;
864 - }
865 -
866 175 2 int git_path_apply_relative(git_buf *target, const char *relpath)
867 - {
868 175 2-4,6 return git_buf_joinpath(target, git_buf_cstr(target), relpath) ||
869 175 5 git_path_resolve_relative(target, 0);
870 - }
871 -
872 28279 2 int git_path_cmp(
873 - const char *name1, size_t len1, int isdir1,
874 - const char *name2, size_t len2, int isdir2,
875 - int (*compare)(const char *, const char *, size_t))
876 - {
877 - unsigned char c1, c2;
878 28279 2 size_t len = len1 < len2 ? len1 : len2;
879 - int cmp;
880 -
881 28279 2 cmp = compare(name1, name2, len);
882 28279 3 if (cmp)
883 27992 4 return cmp;
884 -
885 287 5 c1 = name1[len];
886 287 5 c2 = name2[len];
887 -
888 287 5,6 if (c1 == '\0' && isdir1)
889 113 7 c1 = '/';
890 -
891 287 8,9 if (c2 == '\0' && isdir2)
892 129 10 c2 = '/';
893 -
894 287 11 return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
895 - }
896 -
897 705 2 size_t git_path_common_dirlen(const char *one, const char *two)
898 - {
899 705 2 const char *p, *q, *dirsep = NULL;
900 -
901 4505 2,8-10 for (p = one, q = two; *p && *q; p++, q++) {
902 4220 3,4 if (*p == '/' && *q == '/')
903 719 5 dirsep = p;
904 3501 6 else if (*p != *q)
905 420 7 break;
906 - }
907 -
908 705 11 return dirsep ? (dirsep - one) + 1 : 0;
909 - }
910 -
911 75 2 int git_path_make_relative(git_buf *path, const char *parent)
912 - {
913 - const char *p, *q, *p_dirsep, *q_dirsep;
914 75 2 size_t plen = path->size, newlen, alloclen, depth = 1, i, offset;
915 -
916 2174 2,8-10 for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
917 2164 3,4 if (*p == '/' && *q == '/') {
918 254 5 p_dirsep = p;
919 254 5 q_dirsep = q;
920 - }
921 1910 6 else if (*p != *q)
922 65 7 break;
923 - }
924 -
925 - /* need at least 1 common path segment */
926 75 11-13 if ((p_dirsep == path->ptr || q_dirsep == parent) &&
927 9 13,14 (*p_dirsep != '/' || *q_dirsep != '/')) {
928 6 15 git_error_set(GIT_ERROR_INVALID,
929 - "%s is not a parent of %s", parent, path->ptr);
930 6 16 return GIT_ENOTFOUND;
931 - }
932 -
933 69 17,18 if (*p == '/' && !*q)
934 3 19 p++;
935 66 20,21 else if (!*p && *q == '/')
936 2 22 q++;
937 64 23,24 else if (!*p && !*q)
938 1 25,26 return git_buf_clear(path), 0;
939 - else {
940 63 27 p = p_dirsep + 1;
941 63 27 q = q_dirsep + 1;
942 - }
943 -
944 68 28 plen -= (p - path->ptr);
945 -
946 68 28 if (!*q)
947 7 29 return git_buf_set(path, p, plen);
948 -
949 114 30-33 for (; (q = strchr(q, '/')) && *(q + 1); q++)
950 53 31 depth++;
951 -
952 61 34-40 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3);
953 61 41-47 GIT_ERROR_CHECK_ALLOC_ADD(&newlen, newlen, plen);
954 -
955 61 48-54 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, newlen, 1);
956 -
957 - /* save the offset as we might realllocate the pointer */
958 61 55 offset = p - path->ptr;
959 61 55,56 if (git_buf_try_grow(path, alloclen, 1) < 0)
960 ##### 57 return -1;
961 61 58 p = path->ptr + offset;
962 -
963 61 58 memmove(path->ptr + (depth * 3), p, plen + 1);
964 -
965 175 58-60 for (i = 0; i < depth; i++)
966 114 59 memcpy(path->ptr + (i * 3), "../", 3);
967 -
968 61 61 path->size = newlen;
969 61 61 return 0;
970 - }
971 -
972 ##### 2 bool git_path_has_non_ascii(const char *path, size_t pathlen)
973 - {
974 ##### 2 const uint8_t *scan = (const uint8_t *)path, *end;
975 -
976 ##### 2,5,6 for (end = scan + pathlen; scan < end; ++scan)
977 ##### 3 if (*scan & 0x80)
978 ##### 4 return true;
979 -
980 ##### 7 return false;
981 - }
982 -
983 - #ifdef GIT_USE_ICONV
984 -
985 - int git_path_iconv_init_precompose(git_path_iconv_t *ic)
986 - {
987 - git_buf_init(&ic->buf, 0);
988 - ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
989 - return 0;
990 - }
991 -
992 - void git_path_iconv_clear(git_path_iconv_t *ic)
993 - {
994 - if (ic) {
995 - if (ic->map != (iconv_t)-1)
996 - iconv_close(ic->map);
997 - git_buf_dispose(&ic->buf);
998 - }
999 - }
1000 -
1001 - int git_path_iconv(git_path_iconv_t *ic, const char **in, size_t *inlen)
1002 - {
1003 - char *nfd = (char*)*in, *nfc;
1004 - size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv;
1005 - int retry = 1;
1006 -
1007 - if (!ic || ic->map == (iconv_t)-1 ||
1008 - !git_path_has_non_ascii(*in, *inlen))
1009 - return 0;
1010 -
1011 - git_buf_clear(&ic->buf);
1012 -
1013 - while (1) {
1014 - GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1);
1015 - if (git_buf_grow(&ic->buf, alloclen) < 0)
1016 - return -1;
1017 -
1018 - nfc = ic->buf.ptr + ic->buf.size;
1019 - nfclen = ic->buf.asize - ic->buf.size;
1020 -
1021 - rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
1022 -
1023 - ic->buf.size = (nfc - ic->buf.ptr);
1024 -
1025 - if (rv != (size_t)-1)
1026 - break;
1027 -
1028 - /* if we cannot convert the data (probably because iconv thinks
1029 - * it is not valid UTF-8 source data), then use original data
1030 - */
1031 - if (errno != E2BIG)
1032 - return 0;
1033 -
1034 - /* make space for 2x the remaining data to be converted
1035 - * (with per retry overhead to avoid infinite loops)
1036 - */
1037 - wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
1038 -
1039 - if (retry++ > 4)
1040 - goto fail;
1041 - }
1042 -
1043 - ic->buf.ptr[ic->buf.size] = '\0';
1044 -
1045 - *in = ic->buf.ptr;
1046 - *inlen = ic->buf.size;
1047 -
1048 - return 0;
1049 -
1050 - fail:
1051 - git_error_set(GIT_ERROR_OS, "unable to convert unicode path data");
1052 - return -1;
1053 - }
1054 -
1055 - static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
1056 - static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
1057 -
1058 - /* Check if the platform is decomposing unicode data for us. We will
1059 - * emulate core Git and prefer to use precomposed unicode data internally
1060 - * on these platforms, composing the decomposed unicode on the fly.
1061 - *
1062 - * This mainly happens on the Mac where HDFS stores filenames as
1063 - * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
1064 - * return decomposed unicode from readdir() even when the actual
1065 - * filesystem is storing precomposed unicode.
1066 - */
1067 - bool git_path_does_fs_decompose_unicode(const char *root)
1068 - {
1069 - git_buf path = GIT_BUF_INIT;
1070 - int fd;
1071 - bool found_decomposed = false;
1072 - char tmp[6];
1073 -
1074 - /* Create a file using a precomposed path and then try to find it
1075 - * using the decomposed name. If the lookup fails, then we will mark
1076 - * that we should precompose unicode for this repository.
1077 - */
1078 - if (git_buf_joinpath(&path, root, nfc_file) < 0 ||
1079 - (fd = p_mkstemp(path.ptr)) < 0)
1080 - goto done;
1081 - p_close(fd);
1082 -
1083 - /* record trailing digits generated by mkstemp */
1084 - memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
1085 -
1086 - /* try to look up as NFD path */
1087 - if (git_buf_joinpath(&path, root, nfd_file) < 0)
1088 - goto done;
1089 - memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
1090 -
1091 - found_decomposed = git_path_exists(path.ptr);
1092 -
1093 - /* remove temporary file (using original precomposed path) */
1094 - if (git_buf_joinpath(&path, root, nfc_file) < 0)
1095 - goto done;
1096 - memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
1097 -
1098 - (void)p_unlink(path.ptr);
1099 -
1100 - done:
1101 - git_buf_dispose(&path);
1102 - return found_decomposed;
1103 - }
1104 -
1105 - #else
1106 -
1107 1 2 bool git_path_does_fs_decompose_unicode(const char *root)
1108 - {
1109 - GIT_UNUSED(root);
1110 1 2 return false;
1111 - }
1112 -
1113 - #endif
1114 -
1115 - #if defined(__sun) || defined(__GNU__)
1116 - typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
1117 - #else
1118 - typedef struct dirent path_dirent_data;
1119 - #endif
1120 -
1121 8796 2 int git_path_direach(
1122 - git_buf *path,
1123 - uint32_t flags,
1124 - int (*fn)(void *, git_buf *),
1125 - void *arg)
1126 - {
1127 8796 2 int error = 0;
1128 - ssize_t wd_len;
1129 - DIR *dir;
1130 - struct dirent *de;
1131 -
1132 - #ifdef GIT_USE_ICONV
1133 - git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
1134 - #endif
1135 -
1136 - GIT_UNUSED(flags);
1137 -
1138 8796 2,3 if (git_path_to_dir(path) < 0)
1139 ##### 4 return -1;
1140 -
1141 8790 5 wd_len = git_buf_len(path);
1142 -
1143 8797 6,7 if ((dir = opendir(path->ptr)) == NULL) {
1144 ##### 8 git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path->ptr);
1145 ##### 9,10 if (errno == ENOENT)
1146 ##### 11 return GIT_ENOTFOUND;
1147 -
1148 ##### 12 return -1;
1149 - }
1150 -
1151 - #ifdef GIT_USE_ICONV
1152 - if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
1153 - (void)git_path_iconv_init_precompose(&ic);
1154 - #endif
1155 -
1156 52950 13,28,29 while ((de = readdir(dir)) != NULL) {
1157 44288 14 const char *de_path = de->d_name;
1158 44288 14 size_t de_len = strlen(de_path);
1159 -
1160 44308 14,15 if (git_path_is_dot_or_dotdot(de_path))
1161 17571 16 continue;
1162 -
1163 - #ifdef GIT_USE_ICONV
1164 - if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
1165 - break;
1166 - #endif
1167 -
1168 26737 17,18 if ((error = git_buf_put(path, de_path, de_len)) < 0)
1169 ##### 19 break;
1170 -
1171 26625 20 git_error_clear();
1172 26557 21 error = fn(arg, path);
1173 -
1174 26598 22 git_buf_truncate(path, wd_len); /* restore path */
1175 -
1176 - /* Only set our own error if the callback did not set one already */
1177 26564 23 if (error != 0) {
1178 64 24,25 if (!git_error_last())
1179 60 26 git_error_set_after_callback(error);
1180 -
1181 64 27 break;
1182 - }
1183 - }
1184 -
1185 8726 30 closedir(dir);
1186 -
1187 - #ifdef GIT_USE_ICONV
1188 - git_path_iconv_clear(&ic);
1189 - #endif
1190 -
1191 8795 31 return error;
1192 - }
1193 -
1194 - #if defined(GIT_WIN32) && !defined(__MINGW32__)
1195 -
1196 - /* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
1197 - * and better.
1198 - */
1199 - #ifndef FIND_FIRST_EX_LARGE_FETCH
1200 - # define FIND_FIRST_EX_LARGE_FETCH 2
1201 - #endif
1202 -
1203 - int git_path_diriter_init(
1204 - git_path_diriter *diriter,
1205 - const char *path,
1206 - unsigned int flags)
1207 - {
1208 - git_win32_path path_filter;
1209 -
1210 - static int is_win7_or_later = -1;
1211 - if (is_win7_or_later < 0)
1212 - is_win7_or_later = git_has_win32_version(6, 1, 0);
1213 -
1214 - assert(diriter && path);
1215 -
1216 - memset(diriter, 0, sizeof(git_path_diriter));
1217 - diriter->handle = INVALID_HANDLE_VALUE;
1218 -
1219 - if (git_buf_puts(&diriter->path_utf8, path) < 0)
1220 - return -1;
1221 -
1222 - path_trim_slashes(&diriter->path_utf8);
1223 -
1224 - if (diriter->path_utf8.size == 0) {
1225 - git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path);
1226 - return -1;
1227 - }
1228 -
1229 - if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 ||
1230 - !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) {
1231 - git_error_set(GIT_ERROR_OS, "could not parse the directory path '%s'", path);
1232 - return -1;
1233 - }
1234 -
1235 - diriter->handle = FindFirstFileExW(
1236 - path_filter,
1237 - is_win7_or_later ? FindExInfoBasic : FindExInfoStandard,
1238 - &diriter->current,
1239 - FindExSearchNameMatch,
1240 - NULL,
1241 - is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0);
1242 -
1243 - if (diriter->handle == INVALID_HANDLE_VALUE) {
1244 - git_error_set(GIT_ERROR_OS, "could not open directory '%s'", path);
1245 - return -1;
1246 - }
1247 -
1248 - diriter->parent_utf8_len = diriter->path_utf8.size;
1249 - diriter->flags = flags;
1250 - return 0;
1251 - }
1252 -
1253 - static int diriter_update_paths(git_path_diriter *diriter)
1254 - {
1255 - size_t filename_len, path_len;
1256 -
1257 - filename_len = wcslen(diriter->current.cFileName);
1258 -
1259 - if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) ||
1260 - GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2))
1261 - return -1;
1262 -
1263 - if (path_len > GIT_WIN_PATH_UTF16) {
1264 - git_error_set(GIT_ERROR_FILESYSTEM,
1265 - "invalid path '%.*ls\\%ls' (path too long)",
1266 - diriter->parent_len, diriter->path, diriter->current.cFileName);
1267 - return -1;
1268 - }
1269 -
1270 - diriter->path[diriter->parent_len] = L'\\';
1271 - memcpy(&diriter->path[diriter->parent_len+1],
1272 - diriter->current.cFileName, filename_len * sizeof(wchar_t));
1273 - diriter->path[path_len-1] = L'\0';
1274 -
1275 - git_buf_truncate(&diriter->path_utf8, diriter->parent_utf8_len);
1276 -
1277 - if (diriter->parent_utf8_len > 0 &&
1278 - diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/')
1279 - git_buf_putc(&diriter->path_utf8, '/');
1280 -
1281 - git_buf_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len);
1282 -
1283 - if (git_buf_oom(&diriter->path_utf8))
1284 - return -1;
1285 -
1286 - return 0;
1287 - }
1288 -
1289 - int git_path_diriter_next(git_path_diriter *diriter)
1290 - {
1291 - bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
1292 -
1293 - do {
1294 - /* Our first time through, we already have the data from
1295 - * FindFirstFileW. Use it, otherwise get the next file.
1296 - */
1297 - if (!diriter->needs_next)
1298 - diriter->needs_next = 1;
1299 - else if (!FindNextFileW(diriter->handle, &diriter->current))
1300 - return GIT_ITEROVER;
1301 - } while (skip_dot && git_path_is_dot_or_dotdotW(diriter->current.cFileName));
1302 -
1303 - if (diriter_update_paths(diriter) < 0)
1304 - return -1;
1305 -
1306 - return 0;
1307 - }
1308 -
1309 - int git_path_diriter_filename(
1310 - const char **out,
1311 - size_t *out_len,
1312 - git_path_diriter *diriter)
1313 - {
1314 - assert(out && out_len && diriter);
1315 -
1316 - assert(diriter->path_utf8.size > diriter->parent_utf8_len);
1317 -
1318 - *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1];
1319 - *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1;
1320 - return 0;
1321 - }
1322 -
1323 - int git_path_diriter_fullpath(
1324 - const char **out,
1325 - size_t *out_len,
1326 - git_path_diriter *diriter)
1327 - {
1328 - assert(out && out_len && diriter);
1329 -
1330 - *out = diriter->path_utf8.ptr;
1331 - *out_len = diriter->path_utf8.size;
1332 - return 0;
1333 - }
1334 -
1335 - int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
1336 - {
1337 - assert(out && diriter);
1338 -
1339 - return git_win32__file_attribute_to_stat(out,
1340 - (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current,
1341 - diriter->path);
1342 - }
1343 -
1344 - void git_path_diriter_free(git_path_diriter *diriter)
1345 - {
1346 - if (diriter == NULL)
1347 - return;
1348 -
1349 - git_buf_dispose(&diriter->path_utf8);
1350 -
1351 - if (diriter->handle != INVALID_HANDLE_VALUE) {
1352 - FindClose(diriter->handle);
1353 - diriter->handle = INVALID_HANDLE_VALUE;
1354 - }
1355 - }
1356 -
1357 - #else
1358 -
1359 6747 2 int git_path_diriter_init(
1360 - git_path_diriter *diriter,
1361 - const char *path,
1362 - unsigned int flags)
1363 - {
1364 6747 2-4 assert(diriter && path);
1365 -
1366 6747 5 memset(diriter, 0, sizeof(git_path_diriter));
1367 -
1368 6748 5,6 if (git_buf_puts(&diriter->path, path) < 0)
1369 ##### 7 return -1;
1370 -
1371 6748 8 path_trim_slashes(&diriter->path);
1372 -
1373 6748 9 if (diriter->path.size == 0) {
1374 ##### 10 git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path);
1375 ##### 11 return -1;
1376 - }
1377 -
1378 6748 12,13 if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
1379 4 14 git_buf_dispose(&diriter->path);
1380 -
1381 4 15 git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path);
1382 4 16 return -1;
1383 - }
1384 -
1385 - #ifdef GIT_USE_ICONV
1386 - if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
1387 - (void)git_path_iconv_init_precompose(&diriter->ic);
1388 - #endif
1389 -
1390 6743 17 diriter->parent_len = diriter->path.size;
1391 6743 17 diriter->flags = flags;
1392 -
1393 6743 17 return 0;
1394 - }
1395 -
1396 39262 2 int git_path_diriter_next(git_path_diriter *diriter)
1397 - {
1398 - struct dirent *de;
1399 - const char *filename;
1400 - size_t filename_len;
1401 39262 2 bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
1402 39262 2 int error = 0;
1403 -
1404 39262 2,3 assert(diriter);
1405 -
1406 39262 4,5 errno = 0;
1407 -
1408 - do {
1409 52767 6,7 if ((de = readdir(diriter->dir)) == NULL) {
1410 6744 8,9 if (!errno)
1411 6743 10 return GIT_ITEROVER;
1412 -
1413 ##### 11 git_error_set(GIT_ERROR_OS,
1414 - "could not read directory '%s'", diriter->path.ptr);
1415 ##### 12 return -1;
1416 - }
1417 46023 13-15 } while (skip_dot && git_path_is_dot_or_dotdot(de->d_name));
1418 -
1419 32538 16 filename = de->d_name;
1420 32538 16 filename_len = strlen(filename);
1421 -
1422 - #ifdef GIT_USE_ICONV
1423 - if ((diriter->flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0 &&
1424 - (error = git_path_iconv(&diriter->ic, &filename, &filename_len)) < 0)
1425 - return error;
1426 - #endif
1427 -
1428 32538 16 git_buf_truncate(&diriter->path, diriter->parent_len);
1429 -
1430 32534 17,18 if (diriter->parent_len > 0 &&
1431 32532 18 diriter->path.ptr[diriter->parent_len-1] != '/')
1432 32506 19 git_buf_putc(&diriter->path, '/');
1433 -
1434 32527 20 git_buf_put(&diriter->path, filename, filename_len);
1435 -
1436 32531 21,22 if (git_buf_oom(&diriter->path))
1437 ##### 23 return -1;
1438 -
1439 32531 24 return error;
1440 - }
1441 -
1442 3 2 int git_path_diriter_filename(
1443 - const char **out,
1444 - size_t *out_len,
1445 - git_path_diriter *diriter)
1446 - {
1447 3 2-5 assert(out && out_len && diriter);
1448 -
1449 3 6,7 assert(diriter->path.size > diriter->parent_len);
1450 -
1451 3 8 *out = &diriter->path.ptr[diriter->parent_len+1];
1452 3 8 *out_len = diriter->path.size - diriter->parent_len - 1;
1453 3 8 return 0;
1454 - }
1455 -
1456 - 2 suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files)int git_path_diriter_fullpath(
1457 - const char **out,
1458 - size_t *out_len,
1459 - git_path_diriter *diriter)
1460 - {
1461 - 2-5 suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files) assert(out && out_len && diriter);
1462 -
1463 - 6 suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files) *out = diriter->path.ptr;
1464 - 6 suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files) *out_len = diriter->path.size;
1465 - 6 suppressed: function cannot be solved git_path_diriter_fullpath (automatic due to inconsistent arc counts in .gcda files) return 0;
1466 - }
1467 -
1468 - 2 suppressed: function cannot be solved git_path_diriter_stat (automatic due to inconsistent arc counts in .gcda files)int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
1469 - {
1470 - 2-4 suppressed: function cannot be solved git_path_diriter_stat (automatic due to inconsistent arc counts in .gcda files) assert(out && diriter);
1471 -
1472 - 5 suppressed: function cannot be solved git_path_diriter_stat (automatic due to inconsistent arc counts in .gcda files) return git_path_lstat(diriter->path.ptr, out);
1473 - }
1474 -
1475 6747 2 void git_path_diriter_free(git_path_diriter *diriter)
1476 - {
1477 6747 2 if (diriter == NULL)
1478 6748 3,8 return;
1479 -
1480 6747 4 if (diriter->dir) {
1481 6743 5 closedir(diriter->dir);
1482 6744 6 diriter->dir = NULL;
1483 - }
1484 -
1485 - #ifdef GIT_USE_ICONV
1486 - git_path_iconv_clear(&diriter->ic);
1487 - #endif
1488 -
1489 6748 7 git_buf_dispose(&diriter->path);
1490 - }
1491 -
1492 - #endif
1493 -
1494 272 2 int git_path_dirload(
1495 - git_vector *contents,
1496 - const char *path,
1497 - size_t prefix_len,
1498 - uint32_t flags)
1499 - {
1500 272 2 git_path_diriter iter = GIT_PATH_DIRITER_INIT;
1501 - const char *name;
1502 - size_t name_len;
1503 - char *dup;
1504 - int error;
1505 -
1506 272 2-4 assert(contents && path);
1507 -
1508 272 5,6 if ((error = git_path_diriter_init(&iter, path, flags)) < 0)
1509 ##### 7 return error;
1510 -
1511 5119 8,20,21 while ((error = git_path_diriter_next(&iter)) == 0) {
1512 4847 9,10 if ((error = git_path_diriter_fullpath(&name, &name_len, &iter)) < 0)
1513 ##### 11 break;
1514 -
1515 4847 12,13 assert(name_len > prefix_len);
1516 -
1517 4847 14 dup = git__strndup(name + prefix_len, name_len - prefix_len);
1518 4847 15,16 GIT_ERROR_CHECK_ALLOC(dup);
1519 -
1520 4847 17,18 if ((error = git_vector_insert(contents, dup)) < 0)
1521 ##### 19 break;
1522 - }
1523 -
1524 272 22 if (error == GIT_ITEROVER)
1525 272 23 error = 0;
1526 -
1527 272 24 git_path_diriter_free(&iter);
1528 272 25 return error;
1529 - }
1530 -
1531 134 2 int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
1532 - {
1533 134 2,3 if (git_path_is_local_file_url(url_or_path))
1534 60 4 return git_path_fromurl(local_path_out, url_or_path);
1535 - else
1536 74 5 return git_buf_sets(local_path_out, url_or_path);
1537 - }
1538 -
1539 - /* Reject paths like AUX or COM1, or those versions that end in a dot or
1540 - * colon. ("AUX." or "AUX:")
1541 - */
1542 126 2 GIT_INLINE(bool) verify_dospath(
1543 - const char *component,
1544 - size_t len,
1545 - const char dospath[3],
1546 - bool trailing_num)
1547 - {
1548 126 2-4 size_t last = trailing_num ? 4 : 3;
1549 -
1550 126 5-7 if (len < last || git__strncasecmp(component, dospath, 3) != 0)
1551 99 8 return true;
1552 -
1553 27 9-11 if (trailing_num && (component[3] < '1' || component[3] > '9'))
1554 4 12 return true;
1555 -
1556 16 14 return (len > last &&
1557 23 13,15-17 component[last] != '.' &&
1558 10 15 component[last] != ':');
1559 - }
1560 -
1561 409 2 static int32_t next_hfs_char(const char **in, size_t *len)
1562 - {
1563 453 2,10 while (*len) {
1564 - int32_t codepoint;
1565 413 3 int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint);
1566 413 4 if (cp_len < 0)
1567 369 5,9 return -1;
1568 -
1569 408 6 (*in) += cp_len;
1570 408 6 (*len) -= cp_len;
1571 -
1572 - /* these code points are ignored completely */
1573 408 6 switch (codepoint) {
1574 - case 0x200c: /* ZERO WIDTH NON-JOINER */
1575 - case 0x200d: /* ZERO WIDTH JOINER */
1576 - case 0x200e: /* LEFT-TO-RIGHT MARK */
1577 - case 0x200f: /* RIGHT-TO-LEFT MARK */
1578 - case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
1579 - case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
1580 - case 0x202c: /* POP DIRECTIONAL FORMATTING */
1581 - case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
1582 - case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
1583 - case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
1584 - case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
1585 - case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
1586 - case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
1587 - case 0x206e: /* NATIONAL DIGIT SHAPES */
1588 - case 0x206f: /* NOMINAL DIGIT SHAPES */
1589 - case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
1590 44 7 continue;
1591 - }
1592 -
1593 - /* fold into lowercase -- this will only fold characters in
1594 - * the ASCII range, which is perfectly fine, because the
1595 - * git folder name can only be composed of ascii characters
1596 - */
1597 364 8 return git__tolower(codepoint);
1598 - }
1599 40 11 return 0; /* NULL byte -- end of string */
1600 - }
1601 -
1602 105 2 static bool verify_dotgit_hfs_generic(const char *path, size_t len, const char *needle, size_t needle_len)
1603 - {
1604 - size_t i;
1605 - char c;
1606 -
1607 105 2,3 if (next_hfs_char(&path, &len) != '.')
1608 39 4 return true;
1609 -
1610 304 5,9,10 for (i = 0; i < needle_len; i++) {
1611 250 6 c = next_hfs_char(&path, &len);
1612 250 7 if (c != needle[i])
1613 12 8 return true;
1614 - }
1615 -
1616 54 11,12 if (next_hfs_char(&path, &len) != '\0')
1617 19 13 return true;
1618 -
1619 35 14 return false;
1620 - }
1621 -
1622 81 2 static bool verify_dotgit_hfs(const char *path, size_t len)
1623 - {
1624 81 2 return verify_dotgit_hfs_generic(path, len, "git", CONST_STRLEN("git"));
1625 - }
1626 -
1627 94215 2 GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len)
1628 - {
1629 94215 2 git_buf *reserved = git_repository__reserved_names_win32;
1630 94215 2 size_t reserved_len = git_repository__reserved_names_win32_len;
1631 94215 2 size_t start = 0, i;
1632 -
1633 94215 2 if (repo)
1634 19387 3 git_repository__reserved_names(&reserved, &reserved_len, repo, true);
1635 -
1636 281292 4,8,9 for (i = 0; i < reserved_len; i++) {
1637 187751 5 git_buf *r = &reserved[i];
1638 -
1639 187751 5,6 if (len >= r->size &&
1640 143834 6 strncasecmp(path, r->ptr, r->size) == 0) {
1641 674 7 start = r->size;
1642 674 7 break;
1643 - }
1644 - }
1645 -
1646 94228 10 if (!start)
1647 93538 11 return true;
1648 -
1649 - /*
1650 - * Reject paths that start with Windows-style directory separators
1651 - * (".git\") or NTFS alternate streams (".git:") and could be used
1652 - * to write to the ".git" directory on Windows platforms.
1653 - */
1654 690 12,13 if (path[start] == '\\' || path[start] == ':')
1655 6 14 return false;
1656 -
1657 - /* Reject paths like '.git ' or '.git.' */
1658 704 15,19,20 for (i = start; i < len; i++) {
1659 669 16,17 if (path[i] != ' ' && path[i] != '.')
1660 649 18 return true;
1661 - }
1662 -
1663 35 21 return false;
1664 - }
1665 -
1666 - /*
1667 - * Windows paths that end with spaces and/or dots are elided to the
1668 - * path without them for backward compatibility. That is to say
1669 - * that opening file "foo ", "foo." or even "foo . . ." will all
1670 - * map to a filename of "foo". This function identifies spaces and
1671 - * dots at the end of a filename, whether the proper end of the
1672 - * filename (end of string) or a colon (which would indicate a
1673 - * Windows alternate data stream.)
1674 - */
1675 55 2 GIT_INLINE(bool) ntfs_end_of_filename(const char *path)
1676 - {
1677 55 2 const char *c = path;
1678 -
1679 71 9 for (;; c++) {
1680 126 3,4 if (*c == '\0' || *c == ':')
1681 51 5 return true;
1682 75 6,7 if (*c != ' ' && *c != '.')
1683 4 8 return false;
1684 71 9 }
1685 -
1686 - return true;
1687 - }
1688 -
1689 184 2 GIT_INLINE(bool) verify_dotgit_ntfs_generic(const char *name, size_t len, const char *dotgit_name, size_t dotgit_len, const char *shortname_pfix)
1690 - {
1691 - int i, saw_tilde;
1692 -
1693 184 2-4 if (name[0] == '.' && len >= dotgit_len &&
1694 35 4 !strncasecmp(name + 1, dotgit_name, dotgit_len)) {
1695 31 5,6 return !ntfs_end_of_filename(name + dotgit_len + 1);
1696 - }
1697 -
1698 - /* Detect the basic NTFS shortname with the first six chars */
1699 153 7-9 if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
1700 13 9,10 name[7] >= '1' && name[7] <= '4')
1701 13 11,12 return !ntfs_end_of_filename(name + 8);
1702 -
1703 - /* Catch fallback names */
1704 279 13,31,32 for (i = 0, saw_tilde = 0; i < 8; i++) {
1705 268 14 if (name[i] == '\0') {
1706 2 15 return true;
1707 266 16 } else if (saw_tilde) {
1708 35 17,18 if (name[i] < '0' || name[i] > '9')
1709 ##### 19 return true;
1710 231 20 } else if (name[i] == '~') {
1711 18 21,22 if (name[i+1] < '1' || name[i+1] > '9')
1712 5 23 return true;
1713 13 24 saw_tilde = 1;
1714 213 25 } else if (i >= 6) {
1715 ##### 26 return true;
1716 213 27 } else if ((unsigned char)name[i] > 127) {
1717 ##### 28 return true;
1718 213 29 } else if (git__tolower(name[i]) != shortname_pfix[i]) {
1719 122 30 return true;
1720 - }
1721 - }
1722 -
1723 11 33 return !ntfs_end_of_filename(name + i);
1724 - }
1725 -
1726 1236515 2 GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags)
1727 - {
1728 1236515 2,3 if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\')
1729 3 4 return false;
1730 -
1731 1236512 5,6 if ((flags & GIT_PATH_REJECT_SLASH) && c == '/')
1732 3 7 return false;
1733 -
1734 1236509 8 if (flags & GIT_PATH_REJECT_NT_CHARS) {
1735 45 9 if (c < 32)
1736 2 10 return false;
1737 -
1738 43 11 switch (c) {
1739 - case '<':
1740 - case '>':
1741 - case ':':
1742 - case '"':
1743 - case '|':
1744 - case '?':
1745 - case '*':
1746 7 12 return false;
1747 - }
1748 - }
1749 -
1750 1236500 13 return true;
1751 - }
1752 -
1753 - /*
1754 - * Return the length of the common prefix between str and prefix, comparing them
1755 - * case-insensitively (must be ASCII to match).
1756 - */
1757 1 2 GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char *prefix)
1758 - {
1759 1 2 size_t count = 0;
1760 -
1761 12 2,4,5 while (len >0 && tolower(*str) == tolower(*prefix)) {
1762 11 3 count++;
1763 11 3 str++;
1764 11 3 prefix++;
1765 11 3 len--;
1766 - }
1767 -
1768 1 6 return count;
1769 - }
1770 -
1771 - /*
1772 - * We fundamentally don't like some paths when dealing with user-inputted
1773 - * strings (in checkout or ref names): we don't want dot or dot-dot
1774 - * anywhere, we want to avoid writing weird paths on Windows that can't
1775 - * be handled by tools that use the non-\\?\ APIs, we don't want slashes
1776 - * or double slashes at the end of paths that can make them ambiguous.
1777 - *
1778 - * For checkout, we don't want to recurse into ".git" either.
1779 - */
1780 105774 2 static bool verify_component(
1781 - git_repository *repo,
1782 - const char *component,
1783 - size_t len,
1784 - uint16_t mode,
1785 - unsigned int flags)
1786 - {
1787 105774 2 if (len == 0)
1788 11 3 return false;
1789 -
1790 105763 4,5 if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
1791 2812 6 len == 1 && component[0] == '.')
1792 16 7 return false;
1793 -
1794 105747 8,9 if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
1795 4120 10,11 len == 2 && component[0] == '.' && component[1] == '.')
1796 22 12 return false;
1797 -
1798 105725 13,14 if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.')
1799 4 15 return false;
1800 -
1801 105721 16,17 if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ')
1802 5 18 return false;
1803 -
1804 105716 19,20 if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':')
1805 4 21 return false;
1806 -
1807 105712 22 if (flags & GIT_PATH_REJECT_DOS_PATHS) {
1808 27 23,24,26 if (!verify_dospath(component, len, "CON", false) ||
1809 26 25,28 !verify_dospath(component, len, "PRN", false) ||
1810 25 27,30 !verify_dospath(component, len, "AUX", false) ||
1811 19 29,32 !verify_dospath(component, len, "NUL", false) ||
1812 18 31,34 !verify_dospath(component, len, "COM", true) ||
1813 11 33 !verify_dospath(component, len, "LPT", true))
1814 17 35 return false;
1815 - }
1816 -
1817 105695 36 if (flags & GIT_PATH_REJECT_DOT_GIT_HFS) {
1818 81 37,38 if (!verify_dotgit_hfs(component, len))
1819 31 39 return false;
1820 50 40-42 if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_HFS))
1821 2 43 return false;
1822 - }
1823 -
1824 105662 44 if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) {
1825 94226 45,46 if (!verify_dotgit_ntfs(repo, component, len))
1826 41 47 return false;
1827 94185 48-50 if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_NTFS))
1828 3 51 return false;
1829 - }
1830 -
1831 - /* don't bother rerunning the `.git` test if we ran the HFS or NTFS
1832 - * specific tests, they would have already rejected `.git`.
1833 - */
1834 105632 52,53 if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
1835 105593 53,54 (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
1836 11437 54 (flags & GIT_PATH_REJECT_DOT_GIT_LITERAL)) {
1837 13 55,56 if (len >= 4 &&
1838 8 56,57 component[0] == '.' &&
1839 7 57-59 (component[1] == 'g' || component[1] == 'G') &&
1840 7 59-61 (component[2] == 'i' || component[2] == 'I') &&
1841 7 61,62 (component[3] == 't' || component[3] == 'T')) {
1842 7 63 if (len == 4)
1843 6 64 return false;
1844 -
1845 1 65-67 if (S_ISLNK(mode) && common_prefix_icase(component, len, ".gitmodules") == len)
1846 1 68 return false;
1847 - }
1848 - }
1849 -
1850 105625 69 return true;
1851 - }
1852 -
1853 52826 2 GIT_INLINE(unsigned int) dotgit_flags(
1854 - git_repository *repo,
1855 - unsigned int flags)
1856 - {
1857 52826 2 int protectHFS = 0, protectNTFS = 1;
1858 52826 2 int error = 0;
1859 -
1860 52826 2 flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL;
1861 -
1862 - #ifdef __APPLE__
1863 - protectHFS = 1;
1864 - #endif
1865 -
1866 52826 2,3 if (repo && !protectHFS)
1867 16190 4 error = git_repository__configmap_lookup(&protectHFS, repo, GIT_CONFIGMAP_PROTECTHFS);
1868 52829 5,6 if (!error && protectHFS)
1869 57 7 flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
1870 -
1871 52829 8 if (repo)
1872 16190 9 error = git_repository__configmap_lookup(&protectNTFS, repo, GIT_CONFIGMAP_PROTECTNTFS);
1873 52829 10,11 if (!error && protectNTFS)
1874 52827 12 flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
1875 -
1876 52829 13 return flags;
1877 - }
1878 -
1879 58564 2 bool git_path_isvalid(
1880 - git_repository *repo,
1881 - const char *path,
1882 - uint16_t mode,
1883 - unsigned int flags)
1884 - {
1885 - const char *start, *c;
1886 -
1887 - /* Upgrade the ".git" checks based on platform */
1888 58564 2 if ((flags & GIT_PATH_REJECT_DOT_GIT))
1889 52829 3 flags = dotgit_flags(repo, flags);
1890 -
1891 1295336 4,13,14 for (start = c = path; *c; c++) {
1892 1236867 5,6 if (!verify_char(*c, flags))
1893 15 7 return false;
1894 -
1895 1236852 8 if (*c == '/') {
1896 47302 9,10 if (!verify_component(repo, start, (c - start), mode, flags))
1897 73 11 return false;
1898 -
1899 47229 12 start = c+1;
1900 - }
1901 - }
1902 -
1903 58474 15 return verify_component(repo, start, (c - start), mode, flags);
1904 - }
1905 -
1906 6 2 int git_path_normalize_slashes(git_buf *out, const char *path)
1907 - {
1908 - int error;
1909 - char *p;
1910 -
1911 6 2,3 if ((error = git_buf_puts(out, path)) < 0)
1912 ##### 4 return error;
1913 -
1914 113 5,8,9 for (p = out->ptr; *p; p++) {
1915 107 6 if (*p == '\\')
1916 16 7 *p = '/';
1917 - }
1918 -
1919 6 10 return 0;
1920 - }
1921 -
1922 - static const struct {
1923 - const char *file;
1924 - const char *hash;
1925 - size_t filelen;
1926 - } gitfiles[] = {
1927 - { "gitignore", "gi250a", CONST_STRLEN("gitignore") },
1928 - { "gitmodules", "gi7eba", CONST_STRLEN("gitmodules") },
1929 - { "gitattributes", "gi7d29", CONST_STRLEN("gitattributes") }
1930 - };
1931 -
1932 191 2 extern int git_path_is_gitfile(const char *path, size_t pathlen, git_path_gitfile gitfile, git_path_fs fs)
1933 - {
1934 - const char *file, *hash;
1935 - size_t filelen;
1936 -
1937 191 2 if (!(gitfile >= GIT_PATH_GITFILE_GITIGNORE && gitfile < ARRAY_SIZE(gitfiles))) {
1938 2 3 git_error_set(GIT_ERROR_OS, "invalid gitfile for path validation");
1939 2 4 return -1;
1940 - }
1941 -
1942 189 5 file = gitfiles[gitfile].file;
1943 189 5 filelen = gitfiles[gitfile].filelen;
1944 189 5 hash = gitfiles[gitfile].hash;
1945 -
1946 189 5 switch (fs) {
1947 - case GIT_PATH_FS_GENERIC:
1948 67 6,7,9-12 return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash) ||
1949 19 8 !verify_dotgit_hfs_generic(path, pathlen, file, filelen);
1950 - case GIT_PATH_FS_NTFS:
1951 117 13,14 return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash);
1952 - case GIT_PATH_FS_HFS:
1953 5 15,16 return !verify_dotgit_hfs_generic(path, pathlen, file, filelen);
1954 - default:
1955 ##### 17 git_error_set(GIT_ERROR_OS, "invalid filesystem for path validation");
1956 ##### 18 return -1;
1957 - }
1958 - }
1959 -
1960 2813 2 bool git_path_supports_symlinks(const char *dir)
1961 - {
1962 2813 2 git_buf path = GIT_BUF_INIT;
1963 2813 2 bool supported = false;
1964 - struct stat st;
1965 - int fd;
1966 -
1967 2813 2,3,5 if ((fd = git_futils_mktmp(&path, dir, 0666)) < 0 ||
1968 2813 4,7 p_close(fd) < 0 ||
1969 2813 6,9 p_unlink(path.ptr) < 0 ||
1970 2813 8,11 p_symlink("testing", path.ptr) < 0 ||
1971 2813 10 p_lstat(path.ptr, &st) < 0)
1972 - goto done;
1973 -
1974 2813 12 supported = (S_ISLNK(st.st_mode) != 0);
1975 - done:
1976 2813 13 if (path.size)
1977 2813 14 (void)p_unlink(path.ptr);
1978 2813 15 git_buf_dispose(&path);
1979 2813 16 return supported;
1980 - }
1981 -
1982 3366 2 int git_path_validate_system_file_ownership(const char *path)
1983 - {
1984 - #ifndef GIT_WIN32
1985 - GIT_UNUSED(path);
1986 3366 2 return GIT_OK;
1987 - #else
1988 - git_win32_path buf;
1989 - PSID owner_sid;
1990 - PSECURITY_DESCRIPTOR descriptor = NULL;
1991 - HANDLE token;
1992 - TOKEN_USER *info = NULL;
1993 - DWORD err, len;
1994 - int ret;
1995 -
1996 - if (git_win32_path_from_utf8(buf, path) < 0)
1997 - return -1;
1998 -
1999 - err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT,
2000 - OWNER_SECURITY_INFORMATION |
2001 - DACL_SECURITY_INFORMATION,
2002 - &owner_sid, NULL, NULL, NULL, &descriptor);
2003 -
2004 - if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
2005 - ret = GIT_ENOTFOUND;
2006 - goto cleanup;
2007 - }
2008 -
2009 - if (err != ERROR_SUCCESS) {
2010 - git_error_set(GIT_ERROR_OS, "failed to get security information");
2011 - ret = GIT_ERROR;
2012 - goto cleanup;
2013 - }
2014 -
2015 - if (!IsValidSid(owner_sid)) {
2016 - git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown");
2017 - ret = GIT_ERROR;
2018 - goto cleanup;
2019 - }
2020 -
2021 - if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
2022 - IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
2023 - ret = GIT_OK;
2024 - goto cleanup;
2025 - }
2026 -
2027 - /* Obtain current user's SID */
2028 - if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) &&
2029 - !GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
2030 - info = git__malloc(len);
2031 - GIT_ERROR_CHECK_ALLOC(info);
2032 - if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
2033 - git__free(info);
2034 - info = NULL;
2035 - }
2036 - }
2037 -
2038 - /*
2039 - * If the file is owned by the same account that is running the current
2040 - * process, it's okay to read from that file.
2041 - */
2042 - if (info && EqualSid(owner_sid, info->User.Sid))
2043 - ret = GIT_OK;
2044 - else {
2045 - git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid");
2046 - ret = GIT_ERROR;
2047 - }
2048 - free(info);
2049 -
2050 - cleanup:
2051 - if (descriptor)
2052 - LocalFree(descriptor);
2053 -
2054 - return ret;
2055 - #endif
2056 - }