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 
dos_drive_prefix_length(const char * path)24 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 	if (!(0x80 & (unsigned char)*path))
33 		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 	for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
44 		; /* skip first UTF-8 character */
45 	return path[i] == ':' ? i + 1 : 0;
46 }
47 
48 #ifdef GIT_WIN32
looks_like_network_computer_name(const char * path,int pos)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  */
git_path_basename_r(git_buf * buffer,const char * path)96 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 	if (path == NULL || *path == '\0') {
103 		startp = ".";
104 		len		= 1;
105 		goto Exit;
106 	}
107 
108 	/* Strip trailing slashes */
109 	endp = path + strlen(path) - 1;
110 	while (endp > path && *endp == '/')
111 		endp--;
112 
113 	/* All slashes becomes "/" */
114 	if (endp == path && *endp == '/') {
115 		startp = "/";
116 		len	= 1;
117 		goto Exit;
118 	}
119 
120 	/* Find the start of the base */
121 	startp = endp;
122 	while (startp > path && *(startp - 1) != '/')
123 		startp--;
124 
125 	/* Cast is safe because max path < max int */
126 	len = (int)(endp - startp + 1);
127 
128 Exit:
129 	result = len;
130 
131 	if (buffer != NULL && git_buf_set(buffer, startp, len) < 0)
132 		return -1;
133 
134 	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  */
win32_prefix_length(const char * path,int len)141 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 	return -1;
163 }
164 
165 /*
166  * Based on the Android implementation, BSD licensed.
167  * Check http://android.git.kernel.org/
168  */
git_path_dirname_r(git_buf * buffer,const char * path)169 int git_path_dirname_r(git_buf *buffer, const char *path)
170 {
171 	const char *endp;
172 	int is_prefix = 0, len;
173 
174 	/* Empty or NULL string gets treated as "." */
175 	if (path == NULL || *path == '\0') {
176 		path = ".";
177 		len = 1;
178 		goto Exit;
179 	}
180 
181 	/* Strip trailing slashes */
182 	endp = path + strlen(path) - 1;
183 	while (endp > path && *endp == '/')
184 		endp--;
185 
186 	if (endp - path + 1 > INT_MAX) {
187 		git_error_set(GIT_ERROR_INVALID, "path too long");
188 		len = -1;
189 		goto Exit;
190 	}
191 
192 	if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
193 		is_prefix = 1;
194 		goto Exit;
195 	}
196 
197 	/* Find the start of the dir */
198 	while (endp > path && *endp != '/')
199 		endp--;
200 
201 	/* Either the dir is "/" or there are no slashes */
202 	if (endp == path) {
203 		path = (*endp == '/') ? "/" : ".";
204 		len = 1;
205 		goto Exit;
206 	}
207 
208 	do {
209 		endp--;
210 	} while (endp > path && *endp == '/');
211 
212 	if (endp - path + 1 > INT_MAX) {
213 		git_error_set(GIT_ERROR_INVALID, "path too long");
214 		len = -1;
215 		goto Exit;
216 	}
217 
218 	if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
219 		is_prefix = 1;
220 		goto Exit;
221 	}
222 
223 	/* Cast is safe because max path < max int */
224 	len = (int)(endp - path + 1);
225 
226 Exit:
227 	if (buffer) {
228 		if (git_buf_set(buffer, path, len) < 0)
229 			return -1;
230 		if (is_prefix && git_buf_putc(buffer, '/') < 0)
231 			return -1;
232 	}
233 
234 	return len;
235 }
236 
237 
git_path_dirname(const char * path)238 char *git_path_dirname(const char *path)
239 {
240 	git_buf buf = GIT_BUF_INIT;
241 	char *dirname;
242 
243 	git_path_dirname_r(&buf, path);
244 	dirname = git_buf_detach(&buf);
245 	git_buf_dispose(&buf); /* avoid memleak if error occurs */
246 
247 	return dirname;
248 }
249 
git_path_basename(const char * path)250 char *git_path_basename(const char *path)
251 {
252 	git_buf buf = GIT_BUF_INIT;
253 	char *basename;
254 
255 	git_path_basename_r(&buf, path);
256 	basename = git_buf_detach(&buf);
257 	git_buf_dispose(&buf); /* avoid memleak if error occurs */
258 
259 	return basename;
260 }
261 
git_path_basename_offset(git_buf * buffer)262 size_t git_path_basename_offset(git_buf *buffer)
263 {
264 	ssize_t slash;
265 
266 	if (!buffer || buffer->size <= 0)
267 		return 0;
268 
269 	slash = git_buf_rfind_next(buffer, '/');
270 
271 	if (slash >= 0 && buffer->ptr[slash] == '/')
272 		return (size_t)(slash + 1);
273 
274 	return 0;
275 }
276 
git_path_topdir(const char * path)277 const char *git_path_topdir(const char *path)
278 {
279 	size_t len;
280 	ssize_t i;
281 
282 	assert(path);
283 	len = strlen(path);
284 
285 	if (!len || path[len - 1] != '/')
286 		return NULL;
287 
288 	for (i = (ssize_t)len - 2; i >= 0; --i)
289 		if (path[i] == '/')
290 			break;
291 
292 	return &path[i + 1];
293 }
294 
git_path_root(const char * path)295 int git_path_root(const char *path)
296 {
297 	int offset = 0, prefix_len;
298 
299 	/* Does the root of the path look like a windows drive ? */
300 	if ((prefix_len = dos_drive_prefix_length(path)))
301 		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 	if (path[offset] == '/')
320 		return offset;
321 
322 	return -1;	/* Not a real error - signals that path is not rooted */
323 }
324 
git_path_trim_slashes(git_buf * path)325 void git_path_trim_slashes(git_buf *path)
326 {
327 	int ceiling = git_path_root(path->ptr) + 1;
328 	assert(ceiling >= 0);
329 
330 	while (path->size > (size_t)ceiling) {
331 		if (path->ptr[path->size-1] != '/')
332 			break;
333 
334 		path->ptr[path->size-1] = '\0';
335 		path->size--;
336 	}
337 }
338 
git_path_join_unrooted(git_buf * path_out,const char * path,const char * base,ssize_t * root_at)339 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 	assert(path && path_out);
345 
346 	root = (ssize_t)git_path_root(path);
347 
348 	if (base != NULL && root < 0) {
349 		if (git_buf_joinpath(path_out, base, path) < 0)
350 			return -1;
351 
352 		root = (ssize_t)strlen(base);
353 	} else {
354 		if (git_buf_sets(path_out, path) < 0)
355 			return -1;
356 
357 		if (root < 0)
358 			root = 0;
359 		else if (base)
360 			git_path_equal_or_prefixed(base, path, &root);
361 	}
362 
363 	if (root_at)
364 		*root_at = root;
365 
366 	return 0;
367 }
368 
git_path_squash_slashes(git_buf * path)369 void git_path_squash_slashes(git_buf *path)
370 {
371 	char *p, *q;
372 
373 	if (path->size == 0)
374 		return;
375 
376 	for (p = path->ptr, q = path->ptr; *q; p++, q++) {
377 		*p = *q;
378 
379 		while (*q == '/' && *(q+1) == '/') {
380 			path->size--;
381 			q++;
382 		}
383 	}
384 
385 	*p = '\0';
386 }
387 
git_path_prettify(git_buf * path_out,const char * path,const char * base)388 int git_path_prettify(git_buf *path_out, const char *path, const char *base)
389 {
390 	char buf[GIT_PATH_MAX];
391 
392 	assert(path && path_out);
393 
394 	/* construct path if needed */
395 	if (base != NULL && git_path_root(path) < 0) {
396 		if (git_buf_joinpath(path_out, base, path) < 0)
397 			return -1;
398 		path = path_out->ptr;
399 	}
400 
401 	if (p_realpath(path, buf) == NULL) {
402 		/* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */
403 		int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;
404 		git_error_set(GIT_ERROR_OS, "failed to resolve path '%s'", path);
405 
406 		git_buf_clear(path_out);
407 
408 		return error;
409 	}
410 
411 	return git_buf_sets(path_out, buf);
412 }
413 
git_path_prettify_dir(git_buf * path_out,const char * path,const char * base)414 int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)
415 {
416 	int error = git_path_prettify(path_out, path, base);
417 	return (error < 0) ? error : git_path_to_dir(path_out);
418 }
419 
git_path_to_dir(git_buf * path)420 int git_path_to_dir(git_buf *path)
421 {
422 	if (path->asize > 0 &&
423 		git_buf_len(path) > 0 &&
424 		path->ptr[git_buf_len(path) - 1] != '/')
425 		git_buf_putc(path, '/');
426 
427 	return git_buf_oom(path) ? -1 : 0;
428 }
429 
git_path_string_to_dir(char * path,size_t size)430 void git_path_string_to_dir(char* path, size_t size)
431 {
432 	size_t end = strlen(path);
433 
434 	if (end && path[end - 1] != '/' && end < size) {
435 		path[end] = '/';
436 		path[end + 1] = '\0';
437 	}
438 }
439 
git__percent_decode(git_buf * decoded_out,const char * input)440 int git__percent_decode(git_buf *decoded_out, const char *input)
441 {
442 	int len, hi, lo, i;
443 	assert(decoded_out && input);
444 
445 	len = (int)strlen(input);
446 	git_buf_clear(decoded_out);
447 
448 	for(i = 0; i < len; i++)
449 	{
450 		char c = input[i];
451 
452 		if (c != '%')
453 			goto append;
454 
455 		if (i >= len - 2)
456 			goto append;
457 
458 		hi = git__fromhex(input[i + 1]);
459 		lo = git__fromhex(input[i + 2]);
460 
461 		if (hi < 0 || lo < 0)
462 			goto append;
463 
464 		c = (char)(hi << 4 | lo);
465 		i += 2;
466 
467 append:
468 		if (git_buf_putc(decoded_out, c) < 0)
469 			return -1;
470 	}
471 
472 	return 0;
473 }
474 
error_invalid_local_file_uri(const char * uri)475 static int error_invalid_local_file_uri(const char *uri)
476 {
477 	git_error_set(GIT_ERROR_CONFIG, "'%s' is not a valid local file URI", uri);
478 	return -1;
479 }
480 
local_file_url_prefixlen(const char * file_url)481 static int local_file_url_prefixlen(const char *file_url)
482 {
483 	int len = -1;
484 
485 	if (git__prefixcmp(file_url, "file://") == 0) {
486 		if (file_url[7] == '/')
487 			len = 8;
488 		else if (git__prefixcmp(file_url + 7, "localhost/") == 0)
489 			len = 17;
490 	}
491 
492 	return len;
493 }
494 
git_path_is_local_file_url(const char * file_url)495 bool git_path_is_local_file_url(const char *file_url)
496 {
497 	return (local_file_url_prefixlen(file_url) > 0);
498 }
499 
git_path_fromurl(git_buf * local_path_out,const char * file_url)500 int git_path_fromurl(git_buf *local_path_out, const char *file_url)
501 {
502 	int offset;
503 
504 	assert(local_path_out && file_url);
505 
506 	if ((offset = local_file_url_prefixlen(file_url)) < 0 ||
507 		file_url[offset] == '\0' || file_url[offset] == '/')
508 		return error_invalid_local_file_uri(file_url);
509 
510 #ifndef GIT_WIN32
511 	offset--;	/* A *nix absolute path starts with a forward slash */
512 #endif
513 
514 	git_buf_clear(local_path_out);
515 	return git__percent_decode(local_path_out, file_url + offset);
516 }
517 
git_path_walk_up(git_buf * path,const char * ceiling,int (* cb)(void * data,const char *),void * data)518 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 	int error = 0;
525 	git_buf iter;
526 	ssize_t stop = 0, scan;
527 	char oldc = '\0';
528 
529 	assert(path && cb);
530 
531 	if (ceiling != NULL) {
532 		if (git__prefixcmp(path->ptr, ceiling) == 0)
533 			stop = (ssize_t)strlen(ceiling);
534 		else
535 			stop = git_buf_len(path);
536 	}
537 	scan = git_buf_len(path);
538 
539 	/* empty path: yield only once */
540 	if (!scan) {
541 		error = cb(data, "");
542 		if (error)
543 			git_error_set_after_callback(error);
544 		return error;
545 	}
546 
547 	iter.ptr = path->ptr;
548 	iter.size = git_buf_len(path);
549 	iter.asize = path->asize;
550 
551 	while (scan >= stop) {
552 		error = cb(data, iter.ptr);
553 		iter.ptr[scan] = oldc;
554 
555 		if (error) {
556 			git_error_set_after_callback(error);
557 			break;
558 		}
559 
560 		scan = git_buf_rfind_next(&iter, '/');
561 		if (scan >= 0) {
562 			scan++;
563 			oldc = iter.ptr[scan];
564 			iter.size = scan;
565 			iter.ptr[scan] = '\0';
566 		}
567 	}
568 
569 	if (scan >= 0)
570 		iter.ptr[scan] = oldc;
571 
572 	/* relative path: yield for the last component */
573 	if (!error && stop == 0 && iter.ptr[0] != '/') {
574 		error = cb(data, "");
575 		if (error)
576 			git_error_set_after_callback(error);
577 	}
578 
579 	return error;
580 }
581 
git_path_exists(const char * path)582 bool git_path_exists(const char *path)
583 {
584 	assert(path);
585 	return p_access(path, F_OK) == 0;
586 }
587 
git_path_isdir(const char * path)588 bool git_path_isdir(const char *path)
589 {
590 	struct stat st;
591 	if (p_stat(path, &st) < 0)
592 		return false;
593 
594 	return S_ISDIR(st.st_mode) != 0;
595 }
596 
git_path_isfile(const char * path)597 bool git_path_isfile(const char *path)
598 {
599 	struct stat st;
600 
601 	assert(path);
602 	if (p_stat(path, &st) < 0)
603 		return false;
604 
605 	return S_ISREG(st.st_mode) != 0;
606 }
607 
git_path_islink(const char * path)608 bool git_path_islink(const char *path)
609 {
610 	struct stat st;
611 
612 	assert(path);
613 	if (p_lstat(path, &st) < 0)
614 		return false;
615 
616 	return S_ISLNK(st.st_mode) != 0;
617 }
618 
619 #ifdef GIT_WIN32
620 
git_path_is_empty_dir(const char * path)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 
path_found_entry(void * payload,git_buf * path)664 static int path_found_entry(void *payload, git_buf *path)
665 {
666 	GIT_UNUSED(payload);
667 	return !git_path_is_dot_or_dotdot(path->ptr);
668 }
669 
git_path_is_empty_dir(const char * path)670 bool git_path_is_empty_dir(const char *path)
671 {
672 	int error;
673 	git_buf dir = GIT_BUF_INIT;
674 
675 	if (!git_path_isdir(path))
676 		return false;
677 
678 	if ((error = git_buf_sets(&dir, path)) != 0)
679 		git_error_clear();
680 	else
681 		error = git_path_direach(&dir, 0, path_found_entry, NULL);
682 
683 	git_buf_dispose(&dir);
684 
685 	return !error;
686 }
687 
688 #endif
689 
git_path_set_error(int errno_value,const char * path,const char * action)690 int git_path_set_error(int errno_value, const char *path, const char *action)
691 {
692 	switch (errno_value) {
693 	case ENOENT:
694 	case ENOTDIR:
695 		git_error_set(GIT_ERROR_OS, "could not find '%s' to %s", path, action);
696 		return GIT_ENOTFOUND;
697 
698 	case EINVAL:
699 	case ENAMETOOLONG:
700 		git_error_set(GIT_ERROR_OS, "invalid path for filesystem '%s'", path);
701 		return GIT_EINVALIDSPEC;
702 
703 	case EEXIST:
704 		git_error_set(GIT_ERROR_OS, "failed %s - '%s' already exists", action, path);
705 		return GIT_EEXISTS;
706 
707 	case EACCES:
708 		git_error_set(GIT_ERROR_OS, "failed %s - '%s' is locked", action, path);
709 		return GIT_ELOCKED;
710 
711 	default:
712 		git_error_set(GIT_ERROR_OS, "could not %s '%s'", action, path);
713 		return -1;
714 	}
715 }
716 
git_path_lstat(const char * path,struct stat * st)717 int git_path_lstat(const char *path, struct stat *st)
718 {
719 	if (p_lstat(path, st) == 0)
720 		return 0;
721 
722 	return git_path_set_error(errno, path, "stat");
723 }
724 
_check_dir_contents(git_buf * dir,const char * sub,bool (* predicate)(const char *))725 static bool _check_dir_contents(
726 	git_buf *dir,
727 	const char *sub,
728 	bool (*predicate)(const char *))
729 {
730 	bool result;
731 	size_t dir_size = git_buf_len(dir);
732 	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 	if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) ||
737 		GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) ||
738 		git_buf_try_grow(dir, alloc_size, false) < 0)
739 		return false;
740 
741 	/* save excursion */
742 	if (git_buf_joinpath(dir, dir->ptr, sub) < 0)
743 		return false;
744 
745 	result = predicate(dir->ptr);
746 
747 	/* restore path */
748 	git_buf_truncate(dir, dir_size);
749 	return result;
750 }
751 
git_path_contains(git_buf * dir,const char * item)752 bool git_path_contains(git_buf *dir, const char *item)
753 {
754 	return _check_dir_contents(dir, item, &git_path_exists);
755 }
756 
git_path_contains_dir(git_buf * base,const char * subdir)757 bool git_path_contains_dir(git_buf *base, const char *subdir)
758 {
759 	return _check_dir_contents(base, subdir, &git_path_isdir);
760 }
761 
git_path_contains_file(git_buf * base,const char * file)762 bool git_path_contains_file(git_buf *base, const char *file)
763 {
764 	return _check_dir_contents(base, file, &git_path_isfile);
765 }
766 
git_path_find_dir(git_buf * dir,const char * path,const char * base)767 int git_path_find_dir(git_buf *dir, const char *path, const char *base)
768 {
769 	int error = git_path_join_unrooted(dir, path, base, NULL);
770 
771 	if (!error) {
772 		char buf[GIT_PATH_MAX];
773 		if (p_realpath(dir->ptr, buf) != NULL)
774 			error = git_buf_sets(dir, buf);
775 	}
776 
777 	/* call dirname if this is not a directory */
778 	if (!error) /* && git_path_isdir(dir->ptr) == false) */
779 		error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0;
780 
781 	if (!error)
782 		error = git_path_to_dir(dir);
783 
784 	return error;
785 }
786 
git_path_resolve_relative(git_buf * path,size_t ceiling)787 int git_path_resolve_relative(git_buf *path, size_t ceiling)
788 {
789 	char *base, *to, *from, *next;
790 	size_t len;
791 
792 	GIT_ERROR_CHECK_ALLOC_BUF(path);
793 
794 	if (ceiling > path->size)
795 		ceiling = path->size;
796 
797 	/* recognize drive prefixes, etc. that should not be backed over */
798 	if (ceiling == 0)
799 		ceiling = git_path_root(path->ptr) + 1;
800 
801 	/* recognize URL prefixes that should not be backed over */
802 	if (ceiling == 0) {
803 		for (next = path->ptr; *next && git__isalpha(*next); ++next);
804 		if (next[0] == ':' && next[1] == '/' && next[2] == '/')
805 			ceiling = (next + 3) - path->ptr;
806 	}
807 
808 	base = to = from = path->ptr + ceiling;
809 
810 	while (*from) {
811 		for (next = from; *next && *next != '/'; ++next);
812 
813 		len = next - from;
814 
815 		if (len == 1 && from[0] == '.')
816 			/* do nothing with singleton dot */;
817 
818 		else if (len == 2 && from[0] == '.' && from[1] == '.') {
819 			/* error out if trying to up one from a hard base */
820 			if (to == base && ceiling != 0) {
821 				git_error_set(GIT_ERROR_INVALID,
822 					"cannot strip root component off url");
823 				return -1;
824 			}
825 
826 			/* no more path segments to strip,
827 			 * use '../' as a new base path */
828 			if (to == base) {
829 				if (*next == '/')
830 					len++;
831 
832 				if (to != from)
833 					memmove(to, from, len);
834 
835 				to += len;
836 				/* this is now the base, can't back up from a
837 				 * relative prefix */
838 				base = to;
839 			} else {
840 				/* back up a path segment */
841 				while (to > base && to[-1] == '/') to--;
842 				while (to > base && to[-1] != '/') to--;
843 			}
844 		} else {
845 			if (*next == '/' && *from != '/')
846 				len++;
847 
848 			if (to != from)
849 				memmove(to, from, len);
850 
851 			to += len;
852 		}
853 
854 		from += len;
855 
856 		while (*from == '/') from++;
857 	}
858 
859 	*to = '\0';
860 
861 	path->size = to - path->ptr;
862 
863 	return 0;
864 }
865 
git_path_apply_relative(git_buf * target,const char * relpath)866 int git_path_apply_relative(git_buf *target, const char *relpath)
867 {
868 	return git_buf_joinpath(target, git_buf_cstr(target), relpath) ||
869 	    git_path_resolve_relative(target, 0);
870 }
871 
git_path_cmp(const char * name1,size_t len1,int isdir1,const char * name2,size_t len2,int isdir2,int (* compare)(const char *,const char *,size_t))872 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 	size_t len = len1 < len2 ? len1 : len2;
879 	int cmp;
880 
881 	cmp = compare(name1, name2, len);
882 	if (cmp)
883 		return cmp;
884 
885 	c1 = name1[len];
886 	c2 = name2[len];
887 
888 	if (c1 == '\0' && isdir1)
889 		c1 = '/';
890 
891 	if (c2 == '\0' && isdir2)
892 		c2 = '/';
893 
894 	return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
895 }
896 
git_path_common_dirlen(const char * one,const char * two)897 size_t git_path_common_dirlen(const char *one, const char *two)
898 {
899 	const char *p, *q, *dirsep = NULL;
900 
901 	for (p = one, q = two; *p && *q; p++, q++) {
902 		if (*p == '/' && *q == '/')
903 			dirsep = p;
904 		else if (*p != *q)
905 			break;
906 	}
907 
908 	return dirsep ? (dirsep - one) + 1 : 0;
909 }
910 
git_path_make_relative(git_buf * path,const char * parent)911 int git_path_make_relative(git_buf *path, const char *parent)
912 {
913 	const char *p, *q, *p_dirsep, *q_dirsep;
914 	size_t plen = path->size, newlen, alloclen, depth = 1, i, offset;
915 
916 	for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
917 		if (*p == '/' && *q == '/') {
918 			p_dirsep = p;
919 			q_dirsep = q;
920 		}
921 		else if (*p != *q)
922 			break;
923 	}
924 
925 	/* need at least 1 common path segment */
926 	if ((p_dirsep == path->ptr || q_dirsep == parent) &&
927 		(*p_dirsep != '/' || *q_dirsep != '/')) {
928 		git_error_set(GIT_ERROR_INVALID,
929 			"%s is not a parent of %s", parent, path->ptr);
930 		return GIT_ENOTFOUND;
931 	}
932 
933 	if (*p == '/' && !*q)
934 		p++;
935 	else if (!*p && *q == '/')
936 		q++;
937 	else if (!*p && !*q)
938 		return git_buf_clear(path), 0;
939 	else {
940 		p = p_dirsep + 1;
941 		q = q_dirsep + 1;
942 	}
943 
944 	plen -= (p - path->ptr);
945 
946 	if (!*q)
947 		return git_buf_set(path, p, plen);
948 
949 	for (; (q = strchr(q, '/')) && *(q + 1); q++)
950 		depth++;
951 
952 	GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3);
953 	GIT_ERROR_CHECK_ALLOC_ADD(&newlen, newlen, plen);
954 
955 	GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, newlen, 1);
956 
957 	/* save the offset as we might realllocate the pointer */
958 	offset = p - path->ptr;
959 	if (git_buf_try_grow(path, alloclen, 1) < 0)
960 		return -1;
961 	p = path->ptr + offset;
962 
963 	memmove(path->ptr + (depth * 3), p, plen + 1);
964 
965 	for (i = 0; i < depth; i++)
966 		memcpy(path->ptr + (i * 3), "../", 3);
967 
968 	path->size = newlen;
969 	return 0;
970 }
971 
git_path_has_non_ascii(const char * path,size_t pathlen)972 bool git_path_has_non_ascii(const char *path, size_t pathlen)
973 {
974 	const uint8_t *scan = (const uint8_t *)path, *end;
975 
976 	for (end = scan + pathlen; scan < end; ++scan)
977 		if (*scan & 0x80)
978 			return true;
979 
980 	return false;
981 }
982 
983 #ifdef GIT_USE_ICONV
984 
git_path_iconv_init_precompose(git_path_iconv_t * ic)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 
git_path_iconv_clear(git_path_iconv_t * ic)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 
git_path_iconv(git_path_iconv_t * ic,const char ** in,size_t * inlen)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  */
git_path_does_fs_decompose_unicode(const char * root)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 
git_path_does_fs_decompose_unicode(const char * root)1107 bool git_path_does_fs_decompose_unicode(const char *root)
1108 {
1109 	GIT_UNUSED(root);
1110 	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 
git_path_direach(git_buf * path,uint32_t flags,int (* fn)(void *,git_buf *),void * arg)1121 int git_path_direach(
1122 	git_buf *path,
1123 	uint32_t flags,
1124 	int (*fn)(void *, git_buf *),
1125 	void *arg)
1126 {
1127 	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 	if (git_path_to_dir(path) < 0)
1139 		return -1;
1140 
1141 	wd_len = git_buf_len(path);
1142 
1143 	if ((dir = opendir(path->ptr)) == NULL) {
1144 		git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path->ptr);
1145 		if (errno == ENOENT)
1146 			return GIT_ENOTFOUND;
1147 
1148 		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 	while ((de = readdir(dir)) != NULL) {
1157 		const char *de_path = de->d_name;
1158 		size_t de_len = strlen(de_path);
1159 
1160 		if (git_path_is_dot_or_dotdot(de_path))
1161 			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 		if ((error = git_buf_put(path, de_path, de_len)) < 0)
1169 			break;
1170 
1171 		git_error_clear();
1172 		error = fn(arg, path);
1173 
1174 		git_buf_truncate(path, wd_len); /* restore path */
1175 
1176 		/* Only set our own error if the callback did not set one already */
1177 		if (error != 0) {
1178 			if (!git_error_last())
1179 				git_error_set_after_callback(error);
1180 
1181 			break;
1182 		}
1183 	}
1184 
1185 	closedir(dir);
1186 
1187 #ifdef GIT_USE_ICONV
1188 	git_path_iconv_clear(&ic);
1189 #endif
1190 
1191 	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 
git_path_diriter_init(git_path_diriter * diriter,const char * path,unsigned int flags)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 	git_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 
diriter_update_paths(git_path_diriter * diriter)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 
git_path_diriter_next(git_path_diriter * diriter)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 
git_path_diriter_filename(const char ** out,size_t * out_len,git_path_diriter * diriter)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 
git_path_diriter_fullpath(const char ** out,size_t * out_len,git_path_diriter * diriter)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 
git_path_diriter_stat(struct stat * out,git_path_diriter * diriter)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 
git_path_diriter_free(git_path_diriter * diriter)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 
git_path_diriter_init(git_path_diriter * diriter,const char * path,unsigned int flags)1359 int git_path_diriter_init(
1360 	git_path_diriter *diriter,
1361 	const char *path,
1362 	unsigned int flags)
1363 {
1364 	assert(diriter && path);
1365 
1366 	memset(diriter, 0, sizeof(git_path_diriter));
1367 
1368 	if (git_buf_puts(&diriter->path, path) < 0)
1369 		return -1;
1370 
1371 	git_path_trim_slashes(&diriter->path);
1372 
1373 	if (diriter->path.size == 0) {
1374 		git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path);
1375 		return -1;
1376 	}
1377 
1378 	if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
1379 		git_buf_dispose(&diriter->path);
1380 
1381 		git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path);
1382 		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 	diriter->parent_len = diriter->path.size;
1391 	diriter->flags = flags;
1392 
1393 	return 0;
1394 }
1395 
git_path_diriter_next(git_path_diriter * diriter)1396 int git_path_diriter_next(git_path_diriter *diriter)
1397 {
1398 	struct dirent *de;
1399 	const char *filename;
1400 	size_t filename_len;
1401 	bool skip_dot = !(diriter->flags & GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
1402 	int error = 0;
1403 
1404 	assert(diriter);
1405 
1406 	errno = 0;
1407 
1408 	do {
1409 		if ((de = readdir(diriter->dir)) == NULL) {
1410 			if (!errno)
1411 				return GIT_ITEROVER;
1412 
1413 			git_error_set(GIT_ERROR_OS,
1414 				"could not read directory '%s'", diriter->path.ptr);
1415 			return -1;
1416 		}
1417 	} while (skip_dot && git_path_is_dot_or_dotdot(de->d_name));
1418 
1419 	filename = de->d_name;
1420 	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 	git_buf_truncate(&diriter->path, diriter->parent_len);
1429 
1430 	if (diriter->parent_len > 0 &&
1431 		diriter->path.ptr[diriter->parent_len-1] != '/')
1432 		git_buf_putc(&diriter->path, '/');
1433 
1434 	git_buf_put(&diriter->path, filename, filename_len);
1435 
1436 	if (git_buf_oom(&diriter->path))
1437 		return -1;
1438 
1439 	return error;
1440 }
1441 
git_path_diriter_filename(const char ** out,size_t * out_len,git_path_diriter * diriter)1442 int git_path_diriter_filename(
1443 	const char **out,
1444 	size_t *out_len,
1445 	git_path_diriter *diriter)
1446 {
1447 	assert(out && out_len && diriter);
1448 
1449 	assert(diriter->path.size > diriter->parent_len);
1450 
1451 	*out = &diriter->path.ptr[diriter->parent_len+1];
1452 	*out_len = diriter->path.size - diriter->parent_len - 1;
1453 	return 0;
1454 }
1455 
git_path_diriter_fullpath(const char ** out,size_t * out_len,git_path_diriter * diriter)1456 int git_path_diriter_fullpath(
1457 	const char **out,
1458 	size_t *out_len,
1459 	git_path_diriter *diriter)
1460 {
1461 	assert(out && out_len && diriter);
1462 
1463 	*out = diriter->path.ptr;
1464 	*out_len = diriter->path.size;
1465 	return 0;
1466 }
1467 
git_path_diriter_stat(struct stat * out,git_path_diriter * diriter)1468 int git_path_diriter_stat(struct stat *out, git_path_diriter *diriter)
1469 {
1470 	assert(out && diriter);
1471 
1472 	return git_path_lstat(diriter->path.ptr, out);
1473 }
1474 
git_path_diriter_free(git_path_diriter * diriter)1475 void git_path_diriter_free(git_path_diriter *diriter)
1476 {
1477 	if (diriter == NULL)
1478 		return;
1479 
1480 	if (diriter->dir) {
1481 		closedir(diriter->dir);
1482 		diriter->dir = NULL;
1483 	}
1484 
1485 #ifdef GIT_USE_ICONV
1486 	git_path_iconv_clear(&diriter->ic);
1487 #endif
1488 
1489 	git_buf_dispose(&diriter->path);
1490 }
1491 
1492 #endif
1493 
git_path_dirload(git_vector * contents,const char * path,size_t prefix_len,uint32_t flags)1494 int git_path_dirload(
1495 	git_vector *contents,
1496 	const char *path,
1497 	size_t prefix_len,
1498 	uint32_t flags)
1499 {
1500 	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 	assert(contents && path);
1507 
1508 	if ((error = git_path_diriter_init(&iter, path, flags)) < 0)
1509 		return error;
1510 
1511 	while ((error = git_path_diriter_next(&iter)) == 0) {
1512 		if ((error = git_path_diriter_fullpath(&name, &name_len, &iter)) < 0)
1513 			break;
1514 
1515 		assert(name_len > prefix_len);
1516 
1517 		dup = git__strndup(name + prefix_len, name_len - prefix_len);
1518 		GIT_ERROR_CHECK_ALLOC(dup);
1519 
1520 		if ((error = git_vector_insert(contents, dup)) < 0)
1521 			break;
1522 	}
1523 
1524 	if (error == GIT_ITEROVER)
1525 		error = 0;
1526 
1527 	git_path_diriter_free(&iter);
1528 	return error;
1529 }
1530 
git_path_from_url_or_path(git_buf * local_path_out,const char * url_or_path)1531 int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
1532 {
1533 	if (git_path_is_local_file_url(url_or_path))
1534 		return git_path_fromurl(local_path_out, url_or_path);
1535 	else
1536 		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  */
verify_dospath(const char * component,size_t len,const char dospath[3],bool trailing_num)1542 GIT_INLINE(bool) verify_dospath(
1543 	const char *component,
1544 	size_t len,
1545 	const char dospath[3],
1546 	bool trailing_num)
1547 {
1548 	size_t last = trailing_num ? 4 : 3;
1549 
1550 	if (len < last || git__strncasecmp(component, dospath, 3) != 0)
1551 		return true;
1552 
1553 	if (trailing_num && (component[3] < '1' || component[3] > '9'))
1554 		return true;
1555 
1556 	return (len > last &&
1557 		component[last] != '.' &&
1558 		component[last] != ':');
1559 }
1560 
next_hfs_char(const char ** in,size_t * len)1561 static int32_t next_hfs_char(const char **in, size_t *len)
1562 {
1563 	while (*len) {
1564 		int32_t codepoint;
1565 		int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint);
1566 		if (cp_len < 0)
1567 			return -1;
1568 
1569 		(*in) += cp_len;
1570 		(*len) -= cp_len;
1571 
1572 		/* these code points are ignored completely */
1573 		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 			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 		return git__tolower(codepoint);
1598 	}
1599 	return 0; /* NULL byte -- end of string */
1600 }
1601 
verify_dotgit_hfs_generic(const char * path,size_t len,const char * needle,size_t needle_len)1602 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 	if (next_hfs_char(&path, &len) != '.')
1608 		return true;
1609 
1610 	for (i = 0; i < needle_len; i++) {
1611 		c = next_hfs_char(&path, &len);
1612 		if (c != needle[i])
1613 			return true;
1614 	}
1615 
1616 	if (next_hfs_char(&path, &len) != '\0')
1617 		return true;
1618 
1619 	return false;
1620 }
1621 
verify_dotgit_hfs(const char * path,size_t len)1622 static bool verify_dotgit_hfs(const char *path, size_t len)
1623 {
1624 	return verify_dotgit_hfs_generic(path, len, "git", CONST_STRLEN("git"));
1625 }
1626 
verify_dotgit_ntfs(git_repository * repo,const char * path,size_t len)1627 GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len)
1628 {
1629 	git_buf *reserved = git_repository__reserved_names_win32;
1630 	size_t reserved_len = git_repository__reserved_names_win32_len;
1631 	size_t start = 0, i;
1632 
1633 	if (repo)
1634 		git_repository__reserved_names(&reserved, &reserved_len, repo, true);
1635 
1636 	for (i = 0; i < reserved_len; i++) {
1637 		git_buf *r = &reserved[i];
1638 
1639 		if (len >= r->size &&
1640 			strncasecmp(path, r->ptr, r->size) == 0) {
1641 			start = r->size;
1642 			break;
1643 		}
1644 	}
1645 
1646 	if (!start)
1647 		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 	if (path[start] == '\\' || path[start] == ':')
1655 		return false;
1656 
1657 	/* Reject paths like '.git ' or '.git.' */
1658 	for (i = start; i < len; i++) {
1659 		if (path[i] != ' ' && path[i] != '.')
1660 			return true;
1661 	}
1662 
1663 	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  */
ntfs_end_of_filename(const char * path)1675 GIT_INLINE(bool) ntfs_end_of_filename(const char *path)
1676 {
1677 	const char *c = path;
1678 
1679 	for (;; c++) {
1680 		if (*c == '\0' || *c == ':')
1681 			return true;
1682 		if (*c != ' ' && *c != '.')
1683 			return false;
1684 	}
1685 
1686 	return true;
1687 }
1688 
verify_dotgit_ntfs_generic(const char * name,size_t len,const char * dotgit_name,size_t dotgit_len,const char * shortname_pfix)1689 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 	if (name[0] == '.' && len >= dotgit_len &&
1694 	    !strncasecmp(name + 1, dotgit_name, dotgit_len)) {
1695 		return !ntfs_end_of_filename(name + dotgit_len + 1);
1696 	}
1697 
1698 	/* Detect the basic NTFS shortname with the first six chars */
1699 	if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
1700 	    name[7] >= '1' && name[7] <= '4')
1701 		return !ntfs_end_of_filename(name + 8);
1702 
1703 	/* Catch fallback names */
1704 	for (i = 0, saw_tilde = 0; i < 8; i++) {
1705 		if (name[i] == '\0') {
1706 			return true;
1707 		} else if (saw_tilde) {
1708 			if (name[i] < '0' || name[i] > '9')
1709 				return true;
1710 		} else if (name[i] == '~') {
1711 			if (name[i+1] < '1' || name[i+1]  > '9')
1712 				return true;
1713 			saw_tilde = 1;
1714 		} else if (i >= 6) {
1715 			return true;
1716 		} else if ((unsigned char)name[i] > 127) {
1717 			return true;
1718 		} else if (git__tolower(name[i]) != shortname_pfix[i]) {
1719 			return true;
1720 		}
1721 	}
1722 
1723 	return !ntfs_end_of_filename(name + i);
1724 }
1725 
verify_char(unsigned char c,unsigned int flags)1726 GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags)
1727 {
1728 	if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\')
1729 		return false;
1730 
1731 	if ((flags & GIT_PATH_REJECT_SLASH) && c == '/')
1732 		return false;
1733 
1734 	if (flags & GIT_PATH_REJECT_NT_CHARS) {
1735 		if (c < 32)
1736 			return false;
1737 
1738 		switch (c) {
1739 		case '<':
1740 		case '>':
1741 		case ':':
1742 		case '"':
1743 		case '|':
1744 		case '?':
1745 		case '*':
1746 			return false;
1747 		}
1748 	}
1749 
1750 	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  */
common_prefix_icase(const char * str,size_t len,const char * prefix)1757 GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char *prefix)
1758 {
1759 	size_t count = 0;
1760 
1761 	while (len >0 && tolower(*str) == tolower(*prefix)) {
1762 		count++;
1763 		str++;
1764 		prefix++;
1765 		len--;
1766 	}
1767 
1768 	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  */
verify_component(git_repository * repo,const char * component,size_t len,uint16_t mode,unsigned int flags)1780 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 	if (len == 0)
1788 		return false;
1789 
1790 	if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
1791 		len == 1 && component[0] == '.')
1792 		return false;
1793 
1794 	if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
1795 		len == 2 && component[0] == '.' && component[1] == '.')
1796 		return false;
1797 
1798 	if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.')
1799 		return false;
1800 
1801 	if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ')
1802 		return false;
1803 
1804 	if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':')
1805 		return false;
1806 
1807 	if (flags & GIT_PATH_REJECT_DOS_PATHS) {
1808 		if (!verify_dospath(component, len, "CON", false) ||
1809 			!verify_dospath(component, len, "PRN", false) ||
1810 			!verify_dospath(component, len, "AUX", false) ||
1811 			!verify_dospath(component, len, "NUL", false) ||
1812 			!verify_dospath(component, len, "COM", true)  ||
1813 			!verify_dospath(component, len, "LPT", true))
1814 			return false;
1815 	}
1816 
1817 	if (flags & GIT_PATH_REJECT_DOT_GIT_HFS) {
1818 		if (!verify_dotgit_hfs(component, len))
1819 			return false;
1820 		if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_HFS))
1821 			return false;
1822 	}
1823 
1824 	if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) {
1825 		if (!verify_dotgit_ntfs(repo, component, len))
1826 			return false;
1827 		if (S_ISLNK(mode) && git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_NTFS))
1828 			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 	if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
1835 	    (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
1836 	    (flags & GIT_PATH_REJECT_DOT_GIT_LITERAL)) {
1837 		if (len >= 4 &&
1838 		    component[0] == '.' &&
1839 		    (component[1] == 'g' || component[1] == 'G') &&
1840 		    (component[2] == 'i' || component[2] == 'I') &&
1841 		    (component[3] == 't' || component[3] == 'T')) {
1842 			if (len == 4)
1843 				return false;
1844 
1845 			if (S_ISLNK(mode) && common_prefix_icase(component, len, ".gitmodules") == len)
1846 				return false;
1847 		}
1848 	    }
1849 
1850 	return true;
1851 }
1852 
dotgit_flags(git_repository * repo,unsigned int flags)1853 GIT_INLINE(unsigned int) dotgit_flags(
1854 	git_repository *repo,
1855 	unsigned int flags)
1856 {
1857 	int protectHFS = 0, protectNTFS = 1;
1858 	int error = 0;
1859 
1860 	flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL;
1861 
1862 #ifdef __APPLE__
1863 	protectHFS = 1;
1864 #endif
1865 
1866 	if (repo && !protectHFS)
1867 		error = git_repository__configmap_lookup(&protectHFS, repo, GIT_CONFIGMAP_PROTECTHFS);
1868 	if (!error && protectHFS)
1869 		flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
1870 
1871 	if (repo)
1872 		error = git_repository__configmap_lookup(&protectNTFS, repo, GIT_CONFIGMAP_PROTECTNTFS);
1873 	if (!error && protectNTFS)
1874 		flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
1875 
1876 	return flags;
1877 }
1878 
git_path_isvalid(git_repository * repo,const char * path,uint16_t mode,unsigned int flags)1879 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 	if ((flags & GIT_PATH_REJECT_DOT_GIT))
1889 		flags = dotgit_flags(repo, flags);
1890 
1891 	for (start = c = path; *c; c++) {
1892 		if (!verify_char(*c, flags))
1893 			return false;
1894 
1895 		if (*c == '/') {
1896 			if (!verify_component(repo, start, (c - start), mode, flags))
1897 				return false;
1898 
1899 			start = c+1;
1900 		}
1901 	}
1902 
1903 	return verify_component(repo, start, (c - start), mode, flags);
1904 }
1905 
git_path_normalize_slashes(git_buf * out,const char * path)1906 int git_path_normalize_slashes(git_buf *out, const char *path)
1907 {
1908 	int error;
1909 	char *p;
1910 
1911 	if ((error = git_buf_puts(out, path)) < 0)
1912 		return error;
1913 
1914 	for (p = out->ptr; *p; p++) {
1915 		if (*p == '\\')
1916 			*p = '/';
1917 	}
1918 
1919 	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 
git_path_is_gitfile(const char * path,size_t pathlen,git_path_gitfile gitfile,git_path_fs fs)1932 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 	if (!(gitfile >= GIT_PATH_GITFILE_GITIGNORE && gitfile < ARRAY_SIZE(gitfiles))) {
1938 		git_error_set(GIT_ERROR_OS, "invalid gitfile for path validation");
1939 		return -1;
1940 	}
1941 
1942 	file = gitfiles[gitfile].file;
1943 	filelen = gitfiles[gitfile].filelen;
1944 	hash = gitfiles[gitfile].hash;
1945 
1946 	switch (fs) {
1947 	case GIT_PATH_FS_GENERIC:
1948 		return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash) ||
1949 		       !verify_dotgit_hfs_generic(path, pathlen, file, filelen);
1950 	case GIT_PATH_FS_NTFS:
1951 		return !verify_dotgit_ntfs_generic(path, pathlen, file, filelen, hash);
1952 	case GIT_PATH_FS_HFS:
1953 		return !verify_dotgit_hfs_generic(path, pathlen, file, filelen);
1954 	default:
1955 		git_error_set(GIT_ERROR_OS, "invalid filesystem for path validation");
1956 		return -1;
1957 	}
1958 }
1959 
git_path_supports_symlinks(const char * dir)1960 bool git_path_supports_symlinks(const char *dir)
1961 {
1962 	git_buf path = GIT_BUF_INIT;
1963 	bool supported = false;
1964 	struct stat st;
1965 	int fd;
1966 
1967 	if ((fd = git_futils_mktmp(&path, dir, 0666)) < 0 ||
1968 	    p_close(fd) < 0 ||
1969 	    p_unlink(path.ptr) < 0 ||
1970 	    p_symlink("testing", path.ptr) < 0 ||
1971 	    p_lstat(path.ptr, &st) < 0)
1972 		goto done;
1973 
1974 	supported = (S_ISLNK(st.st_mode) != 0);
1975 done:
1976 	if (path.size)
1977 		(void)p_unlink(path.ptr);
1978 	git_buf_dispose(&path);
1979 	return supported;
1980 }
1981 
git_path_validate_system_file_ownership(const char * path)1982 int git_path_validate_system_file_ownership(const char *path)
1983 {
1984 #ifndef GIT_WIN32
1985 	GIT_UNUSED(path);
1986 	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 }
2057