2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
11 #include "repository.h"
13 #include "win32/posix.h"
14 #include "win32/w32_buffer.h"
15 #include "win32/w32_util.h"
16 #include "win32/version.h"
24 #define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':')
27 static bool looks_like_network_computer_name(const char *path
, int pos
)
32 if (path
[0] != '/' || path
[1] != '/')
45 * Based on the Android implementation, BSD licensed.
46 * http://android.git.kernel.org/
48 * Copyright (C) 2008 The Android Open Source Project
49 * All rights reserved.
51 * Redistribution and use in source and binary forms, with or without
52 * modification, are permitted provided that the following conditions
54 * * Redistributions of source code must retain the above copyright
55 * notice, this list of conditions and the following disclaimer.
56 * * Redistributions in binary form must reproduce the above copyright
57 * notice, this list of conditions and the following disclaimer in
58 * the documentation and/or other materials provided with the
61 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
62 * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
63 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
64 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
65 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
66 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
67 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
68 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
69 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
70 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
71 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
74 int git_path_basename_r(git_buf
*buffer
, const char *path
)
76 const char *endp
, *startp
;
79 /* Empty or NULL string gets treated as "." */
80 if (path
== NULL
|| *path
== '\0') {
86 /* Strip trailing slashes */
87 endp
= path
+ strlen(path
) - 1;
88 while (endp
> path
&& *endp
== '/')
91 /* All slashes becomes "/" */
92 if (endp
== path
&& *endp
== '/') {
98 /* Find the start of the base */
100 while (startp
> path
&& *(startp
- 1) != '/')
103 /* Cast is safe because max path < max int */
104 len
= (int)(endp
- startp
+ 1);
109 if (buffer
!= NULL
&& git_buf_set(buffer
, startp
, len
) < 0)
116 * Determine if the path is a Windows prefix and, if so, returns
117 * its actual lentgh. If it is not a prefix, returns -1.
119 static int win32_prefix_length(const char *path
, int len
)
126 * Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
129 if (len
== 2 && LOOKS_LIKE_DRIVE_PREFIX(path
))
133 * Similarly checks if we're dealing with a network computer name
134 * '//computername/.git' will return '//computername/'
136 if (looks_like_network_computer_name(path
, len
))
144 * Based on the Android implementation, BSD licensed.
145 * Check http://android.git.kernel.org/
147 int git_path_dirname_r(git_buf
*buffer
, const char *path
)
150 int is_prefix
= 0, len
;
152 /* Empty or NULL string gets treated as "." */
153 if (path
== NULL
|| *path
== '\0') {
159 /* Strip trailing slashes */
160 endp
= path
+ strlen(path
) - 1;
161 while (endp
> path
&& *endp
== '/')
164 if ((len
= win32_prefix_length(path
, endp
- path
+ 1)) > 0) {
169 /* Find the start of the dir */
170 while (endp
> path
&& *endp
!= '/')
173 /* Either the dir is "/" or there are no slashes */
175 path
= (*endp
== '/') ? "/" : ".";
182 } while (endp
> path
&& *endp
== '/');
184 if ((len
= win32_prefix_length(path
, endp
- path
+ 1)) > 0) {
189 /* Cast is safe because max path < max int */
190 len
= (int)(endp
- path
+ 1);
194 if (git_buf_set(buffer
, path
, len
) < 0)
196 if (is_prefix
&& git_buf_putc(buffer
, '/') < 0)
204 char *git_path_dirname(const char *path
)
206 git_buf buf
= GIT_BUF_INIT
;
209 git_path_dirname_r(&buf
, path
);
210 dirname
= git_buf_detach(&buf
);
211 git_buf_dispose(&buf
); /* avoid memleak if error occurs */
216 char *git_path_basename(const char *path
)
218 git_buf buf
= GIT_BUF_INIT
;
221 git_path_basename_r(&buf
, path
);
222 basename
= git_buf_detach(&buf
);
223 git_buf_dispose(&buf
); /* avoid memleak if error occurs */
228 size_t git_path_basename_offset(git_buf
*buffer
)
232 if (!buffer
|| buffer
->size
<= 0)
235 slash
= git_buf_rfind_next(buffer
, '/');
237 if (slash
>= 0 && buffer
->ptr
[slash
] == '/')
238 return (size_t)(slash
+ 1);
243 const char *git_path_topdir(const char *path
)
251 if (!len
|| path
[len
- 1] != '/')
254 for (i
= (ssize_t
)len
- 2; i
>= 0; --i
)
261 int git_path_root(const char *path
)
265 /* Does the root of the path look like a windows drive ? */
266 if (LOOKS_LIKE_DRIVE_PREFIX(path
))
270 /* Are we dealing with a windows network path? */
271 else if ((path
[0] == '/' && path
[1] == '/' && path
[2] != '/') ||
272 (path
[0] == '\\' && path
[1] == '\\' && path
[2] != '\\'))
276 /* Skip the computer name segment */
277 while (path
[offset
] && path
[offset
] != '/' && path
[offset
] != '\\')
282 if (path
[offset
] == '/' || path
[offset
] == '\\')
285 return -1; /* Not a real error - signals that path is not rooted */
288 void git_path_trim_slashes(git_buf
*path
)
290 int ceiling
= git_path_root(path
->ptr
) + 1;
291 assert(ceiling
>= 0);
293 while (path
->size
> (size_t)ceiling
) {
294 if (path
->ptr
[path
->size
-1] != '/')
297 path
->ptr
[path
->size
-1] = '\0';
302 int git_path_join_unrooted(
303 git_buf
*path_out
, const char *path
, const char *base
, ssize_t
*root_at
)
307 assert(path
&& path_out
);
309 root
= (ssize_t
)git_path_root(path
);
311 if (base
!= NULL
&& root
< 0) {
312 if (git_buf_joinpath(path_out
, base
, path
) < 0)
315 root
= (ssize_t
)strlen(base
);
317 if (git_buf_sets(path_out
, path
) < 0)
323 git_path_equal_or_prefixed(base
, path
, &root
);
332 void git_path_squash_slashes(git_buf
*path
)
339 for (p
= path
->ptr
, q
= path
->ptr
; *q
; p
++, q
++) {
342 while (*q
== '/' && *(q
+1) == '/') {
351 int git_path_prettify(git_buf
*path_out
, const char *path
, const char *base
)
353 char buf
[GIT_PATH_MAX
];
355 assert(path
&& path_out
);
357 /* construct path if needed */
358 if (base
!= NULL
&& git_path_root(path
) < 0) {
359 if (git_buf_joinpath(path_out
, base
, path
) < 0)
361 path
= path_out
->ptr
;
364 if (p_realpath(path
, buf
) == NULL
) {
365 /* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */
366 int error
= (errno
== ENOENT
|| errno
== ENOTDIR
) ? GIT_ENOTFOUND
: -1;
367 git_error_set(GIT_ERROR_OS
, "failed to resolve path '%s'", path
);
369 git_buf_clear(path_out
);
374 return git_buf_sets(path_out
, buf
);
377 int git_path_prettify_dir(git_buf
*path_out
, const char *path
, const char *base
)
379 int error
= git_path_prettify(path_out
, path
, base
);
380 return (error
< 0) ? error
: git_path_to_dir(path_out
);
383 int git_path_to_dir(git_buf
*path
)
385 if (path
->asize
> 0 &&
386 git_buf_len(path
) > 0 &&
387 path
->ptr
[git_buf_len(path
) - 1] != '/')
388 git_buf_putc(path
, '/');
390 return git_buf_oom(path
) ? -1 : 0;
393 void git_path_string_to_dir(char* path
, size_t size
)
395 size_t end
= strlen(path
);
397 if (end
&& path
[end
- 1] != '/' && end
< size
) {
399 path
[end
+ 1] = '\0';
403 int git__percent_decode(git_buf
*decoded_out
, const char *input
)
406 assert(decoded_out
&& input
);
408 len
= (int)strlen(input
);
409 git_buf_clear(decoded_out
);
411 for(i
= 0; i
< len
; i
++)
421 hi
= git__fromhex(input
[i
+ 1]);
422 lo
= git__fromhex(input
[i
+ 2]);
424 if (hi
< 0 || lo
< 0)
427 c
= (char)(hi
<< 4 | lo
);
431 if (git_buf_putc(decoded_out
, c
) < 0)
438 static int error_invalid_local_file_uri(const char *uri
)
440 git_error_set(GIT_ERROR_CONFIG
, "'%s' is not a valid local file URI", uri
);
444 static int local_file_url_prefixlen(const char *file_url
)
448 if (git__prefixcmp(file_url
, "file://") == 0) {
449 if (file_url
[7] == '/')
451 else if (git__prefixcmp(file_url
+ 7, "localhost/") == 0)
458 bool git_path_is_local_file_url(const char *file_url
)
460 return (local_file_url_prefixlen(file_url
) > 0);
463 int git_path_fromurl(git_buf
*local_path_out
, const char *file_url
)
467 assert(local_path_out
&& file_url
);
469 if ((offset
= local_file_url_prefixlen(file_url
)) < 0 ||
470 file_url
[offset
] == '\0' || file_url
[offset
] == '/')
471 return error_invalid_local_file_uri(file_url
);
474 offset
--; /* A *nix absolute path starts with a forward slash */
477 git_buf_clear(local_path_out
);
478 return git__percent_decode(local_path_out
, file_url
+ offset
);
481 int git_path_walk_up(
484 int (*cb
)(void *data
, const char *),
489 ssize_t stop
= 0, scan
;
494 if (ceiling
!= NULL
) {
495 if (git__prefixcmp(path
->ptr
, ceiling
) == 0)
496 stop
= (ssize_t
)strlen(ceiling
);
498 stop
= git_buf_len(path
);
500 scan
= git_buf_len(path
);
502 /* empty path: yield only once */
504 error
= cb(data
, "");
506 git_error_set_after_callback(error
);
510 iter
.ptr
= path
->ptr
;
511 iter
.size
= git_buf_len(path
);
512 iter
.asize
= path
->asize
;
514 while (scan
>= stop
) {
515 error
= cb(data
, iter
.ptr
);
516 iter
.ptr
[scan
] = oldc
;
519 git_error_set_after_callback(error
);
523 scan
= git_buf_rfind_next(&iter
, '/');
526 oldc
= iter
.ptr
[scan
];
528 iter
.ptr
[scan
] = '\0';
533 iter
.ptr
[scan
] = oldc
;
535 /* relative path: yield for the last component */
536 if (!error
&& stop
== 0 && iter
.ptr
[0] != '/') {
537 error
= cb(data
, "");
539 git_error_set_after_callback(error
);
545 bool git_path_exists(const char *path
)
548 return p_access(path
, F_OK
) == 0;
551 bool git_path_isdir(const char *path
)
554 if (p_stat(path
, &st
) < 0)
557 return S_ISDIR(st
.st_mode
) != 0;
560 bool git_path_isfile(const char *path
)
565 if (p_stat(path
, &st
) < 0)
568 return S_ISREG(st
.st_mode
) != 0;
571 bool git_path_islink(const char *path
)
576 if (p_lstat(path
, &st
) < 0)
579 return S_ISLNK(st
.st_mode
) != 0;
584 bool git_path_is_empty_dir(const char *path
)
586 git_win32_path filter_w
;
589 if (git_win32__findfirstfile_filter(filter_w
, path
)) {
590 WIN32_FIND_DATAW findData
;
591 HANDLE hFind
= FindFirstFileW(filter_w
, &findData
);
593 /* FindFirstFile will fail if there are no children to the given
594 * path, which can happen if the given path is a file (and obviously
595 * has no children) or if the given path is an empty mount point.
596 * (Most directories have at least directory entries '.' and '..',
597 * but ridiculously another volume mounted in another drive letter's
598 * path space do not, and thus have nothing to enumerate.) If
599 * FindFirstFile fails, check if this is a directory-like thing
602 if (hFind
== INVALID_HANDLE_VALUE
)
603 return git_path_isdir(path
);
605 /* If the find handle was created successfully, then it's a directory */
609 /* Allow the enumeration to return . and .. and still be considered
610 * empty. In the special case of drive roots (i.e. C:\) where . and
611 * .. do not occur, we can still consider the path to be an empty
612 * directory if there's nothing there. */
613 if (!git_path_is_dot_or_dotdotW(findData
.cFileName
)) {
617 } while (FindNextFileW(hFind
, &findData
));
627 static int path_found_entry(void *payload
, git_buf
*path
)
630 return !git_path_is_dot_or_dotdot(path
->ptr
);
633 bool git_path_is_empty_dir(const char *path
)
636 git_buf dir
= GIT_BUF_INIT
;
638 if (!git_path_isdir(path
))
641 if ((error
= git_buf_sets(&dir
, path
)) != 0)
644 error
= git_path_direach(&dir
, 0, path_found_entry
, NULL
);
646 git_buf_dispose(&dir
);
653 int git_path_set_error(int errno_value
, const char *path
, const char *action
)
655 switch (errno_value
) {
658 git_error_set(GIT_ERROR_OS
, "could not find '%s' to %s", path
, action
);
659 return GIT_ENOTFOUND
;
663 git_error_set(GIT_ERROR_OS
, "invalid path for filesystem '%s'", path
);
664 return GIT_EINVALIDSPEC
;
667 git_error_set(GIT_ERROR_OS
, "failed %s - '%s' already exists", action
, path
);
671 git_error_set(GIT_ERROR_OS
, "failed %s - '%s' is locked", action
, path
);
675 git_error_set(GIT_ERROR_OS
, "could not %s '%s'", action
, path
);
680 int git_path_lstat(const char *path
, struct stat
*st
)
682 if (p_lstat(path
, st
) == 0)
685 return git_path_set_error(errno
, path
, "stat");
688 static bool _check_dir_contents(
691 bool (*predicate
)(const char *))
694 size_t dir_size
= git_buf_len(dir
);
695 size_t sub_size
= strlen(sub
);
698 /* leave base valid even if we could not make space for subdir */
699 if (GIT_ADD_SIZET_OVERFLOW(&alloc_size
, dir_size
, sub_size
) ||
700 GIT_ADD_SIZET_OVERFLOW(&alloc_size
, alloc_size
, 2) ||
701 git_buf_try_grow(dir
, alloc_size
, false) < 0)
705 if (git_buf_joinpath(dir
, dir
->ptr
, sub
) < 0)
708 result
= predicate(dir
->ptr
);
711 git_buf_truncate(dir
, dir_size
);
715 bool git_path_contains(git_buf
*dir
, const char *item
)
717 return _check_dir_contents(dir
, item
, &git_path_exists
);
720 bool git_path_contains_dir(git_buf
*base
, const char *subdir
)
722 return _check_dir_contents(base
, subdir
, &git_path_isdir
);
725 bool git_path_contains_file(git_buf
*base
, const char *file
)
727 return _check_dir_contents(base
, file
, &git_path_isfile
);
730 int git_path_find_dir(git_buf
*dir
, const char *path
, const char *base
)
732 int error
= git_path_join_unrooted(dir
, path
, base
, NULL
);
735 char buf
[GIT_PATH_MAX
];
736 if (p_realpath(dir
->ptr
, buf
) != NULL
)
737 error
= git_buf_sets(dir
, buf
);
740 /* call dirname if this is not a directory */
741 if (!error
) /* && git_path_isdir(dir->ptr) == false) */
742 error
= (git_path_dirname_r(dir
, dir
->ptr
) < 0) ? -1 : 0;
745 error
= git_path_to_dir(dir
);
750 int git_path_resolve_relative(git_buf
*path
, size_t ceiling
)
752 char *base
, *to
, *from
, *next
;
755 GIT_ERROR_CHECK_ALLOC_BUF(path
);
757 if (ceiling
> path
->size
)
758 ceiling
= path
->size
;
760 /* recognize drive prefixes, etc. that should not be backed over */
762 ceiling
= git_path_root(path
->ptr
) + 1;
764 /* recognize URL prefixes that should not be backed over */
766 for (next
= path
->ptr
; *next
&& git__isalpha(*next
); ++next
);
767 if (next
[0] == ':' && next
[1] == '/' && next
[2] == '/')
768 ceiling
= (next
+ 3) - path
->ptr
;
771 base
= to
= from
= path
->ptr
+ ceiling
;
774 for (next
= from
; *next
&& *next
!= '/'; ++next
);
778 if (len
== 1 && from
[0] == '.')
779 /* do nothing with singleton dot */;
781 else if (len
== 2 && from
[0] == '.' && from
[1] == '.') {
782 /* error out if trying to up one from a hard base */
783 if (to
== base
&& ceiling
!= 0) {
784 git_error_set(GIT_ERROR_INVALID
,
785 "cannot strip root component off url");
789 /* no more path segments to strip,
790 * use '../' as a new base path */
796 memmove(to
, from
, len
);
799 /* this is now the base, can't back up from a
803 /* back up a path segment */
804 while (to
> base
&& to
[-1] == '/') to
--;
805 while (to
> base
&& to
[-1] != '/') to
--;
808 if (*next
== '/' && *from
!= '/')
812 memmove(to
, from
, len
);
819 while (*from
== '/') from
++;
824 path
->size
= to
- path
->ptr
;
829 int git_path_apply_relative(git_buf
*target
, const char *relpath
)
831 return git_buf_joinpath(target
, git_buf_cstr(target
), relpath
) ||
832 git_path_resolve_relative(target
, 0);
836 const char *name1
, size_t len1
, int isdir1
,
837 const char *name2
, size_t len2
, int isdir2
,
838 int (*compare
)(const char *, const char *, size_t))
840 unsigned char c1
, c2
;
841 size_t len
= len1
< len2
? len1
: len2
;
844 cmp
= compare(name1
, name2
, len
);
851 if (c1
== '\0' && isdir1
)
854 if (c2
== '\0' && isdir2
)
857 return (c1
< c2
) ? -1 : (c1
> c2
) ? 1 : 0;
860 size_t git_path_common_dirlen(const char *one
, const char *two
)
862 const char *p
, *q
, *dirsep
= NULL
;
864 for (p
= one
, q
= two
; *p
&& *q
; p
++, q
++) {
865 if (*p
== '/' && *q
== '/')
871 return dirsep
? (dirsep
- one
) + 1 : 0;
874 int git_path_make_relative(git_buf
*path
, const char *parent
)
876 const char *p
, *q
, *p_dirsep
, *q_dirsep
;
877 size_t plen
= path
->size
, newlen
, alloclen
, depth
= 1, i
, offset
;
879 for (p_dirsep
= p
= path
->ptr
, q_dirsep
= q
= parent
; *p
&& *q
; p
++, q
++) {
880 if (*p
== '/' && *q
== '/') {
888 /* need at least 1 common path segment */
889 if ((p_dirsep
== path
->ptr
|| q_dirsep
== parent
) &&
890 (*p_dirsep
!= '/' || *q_dirsep
!= '/')) {
891 git_error_set(GIT_ERROR_INVALID
,
892 "%s is not a parent of %s", parent
, path
->ptr
);
893 return GIT_ENOTFOUND
;
896 if (*p
== '/' && !*q
)
898 else if (!*p
&& *q
== '/')
901 return git_buf_clear(path
), 0;
907 plen
-= (p
- path
->ptr
);
910 return git_buf_set(path
, p
, plen
);
912 for (; (q
= strchr(q
, '/')) && *(q
+ 1); q
++)
915 GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen
, depth
, 3);
916 GIT_ERROR_CHECK_ALLOC_ADD(&newlen
, newlen
, plen
);
918 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, newlen
, 1);
920 /* save the offset as we might realllocate the pointer */
921 offset
= p
- path
->ptr
;
922 if (git_buf_try_grow(path
, alloclen
, 1) < 0)
924 p
= path
->ptr
+ offset
;
926 memmove(path
->ptr
+ (depth
* 3), p
, plen
+ 1);
928 for (i
= 0; i
< depth
; i
++)
929 memcpy(path
->ptr
+ (i
* 3), "../", 3);
935 bool git_path_has_non_ascii(const char *path
, size_t pathlen
)
937 const uint8_t *scan
= (const uint8_t *)path
, *end
;
939 for (end
= scan
+ pathlen
; scan
< end
; ++scan
)
948 int git_path_iconv_init_precompose(git_path_iconv_t
*ic
)
950 git_buf_init(&ic
->buf
, 0);
951 ic
->map
= iconv_open(GIT_PATH_REPO_ENCODING
, GIT_PATH_NATIVE_ENCODING
);
955 void git_path_iconv_clear(git_path_iconv_t
*ic
)
958 if (ic
->map
!= (iconv_t
)-1)
959 iconv_close(ic
->map
);
960 git_buf_dispose(&ic
->buf
);
964 int git_path_iconv(git_path_iconv_t
*ic
, const char **in
, size_t *inlen
)
966 char *nfd
= (char*)*in
, *nfc
;
967 size_t nfdlen
= *inlen
, nfclen
, wantlen
= nfdlen
, alloclen
, rv
;
970 if (!ic
|| ic
->map
== (iconv_t
)-1 ||
971 !git_path_has_non_ascii(*in
, *inlen
))
974 git_buf_clear(&ic
->buf
);
977 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, wantlen
, 1);
978 if (git_buf_grow(&ic
->buf
, alloclen
) < 0)
981 nfc
= ic
->buf
.ptr
+ ic
->buf
.size
;
982 nfclen
= ic
->buf
.asize
- ic
->buf
.size
;
984 rv
= iconv(ic
->map
, &nfd
, &nfdlen
, &nfc
, &nfclen
);
986 ic
->buf
.size
= (nfc
- ic
->buf
.ptr
);
988 if (rv
!= (size_t)-1)
991 /* if we cannot convert the data (probably because iconv thinks
992 * it is not valid UTF-8 source data), then use original data
997 /* make space for 2x the remaining data to be converted
998 * (with per retry overhead to avoid infinite loops)
1000 wantlen
= ic
->buf
.size
+ max(nfclen
, nfdlen
) * 2 + (size_t)(retry
* 4);
1006 ic
->buf
.ptr
[ic
->buf
.size
] = '\0';
1009 *inlen
= ic
->buf
.size
;
1014 git_error_set(GIT_ERROR_OS
, "unable to convert unicode path data");
1018 static const char *nfc_file
= "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
1019 static const char *nfd_file
= "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
1021 /* Check if the platform is decomposing unicode data for us. We will
1022 * emulate core Git and prefer to use precomposed unicode data internally
1023 * on these platforms, composing the decomposed unicode on the fly.
1025 * This mainly happens on the Mac where HDFS stores filenames as
1026 * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
1027 * return decomposed unicode from readdir() even when the actual
1028 * filesystem is storing precomposed unicode.
1030 bool git_path_does_fs_decompose_unicode(const char *root
)
1032 git_buf path
= GIT_BUF_INIT
;
1034 bool found_decomposed
= false;
1037 /* Create a file using a precomposed path and then try to find it
1038 * using the decomposed name. If the lookup fails, then we will mark
1039 * that we should precompose unicode for this repository.
1041 if (git_buf_joinpath(&path
, root
, nfc_file
) < 0 ||
1042 (fd
= p_mkstemp(path
.ptr
)) < 0)
1046 /* record trailing digits generated by mkstemp */
1047 memcpy(tmp
, path
.ptr
+ path
.size
- sizeof(tmp
), sizeof(tmp
));
1049 /* try to look up as NFD path */
1050 if (git_buf_joinpath(&path
, root
, nfd_file
) < 0)
1052 memcpy(path
.ptr
+ path
.size
- sizeof(tmp
), tmp
, sizeof(tmp
));
1054 found_decomposed
= git_path_exists(path
.ptr
);
1056 /* remove temporary file (using original precomposed path) */
1057 if (git_buf_joinpath(&path
, root
, nfc_file
) < 0)
1059 memcpy(path
.ptr
+ path
.size
- sizeof(tmp
), tmp
, sizeof(tmp
));
1061 (void)p_unlink(path
.ptr
);
1064 git_buf_dispose(&path
);
1065 return found_decomposed
;
1070 bool git_path_does_fs_decompose_unicode(const char *root
)
1078 #if defined(__sun) || defined(__GNU__)
1079 typedef char path_dirent_data
[sizeof(struct dirent
) + FILENAME_MAX
+ 1];
1081 typedef struct dirent path_dirent_data
;
1084 int git_path_direach(
1087 int (*fn
)(void *, git_buf
*),
1095 #ifdef GIT_USE_ICONV
1096 git_path_iconv_t ic
= GIT_PATH_ICONV_INIT
;
1101 if (git_path_to_dir(path
) < 0)
1104 wd_len
= git_buf_len(path
);
1106 if ((dir
= opendir(path
->ptr
)) == NULL
) {
1107 git_error_set(GIT_ERROR_OS
, "failed to open directory '%s'", path
->ptr
);
1108 if (errno
== ENOENT
)
1109 return GIT_ENOTFOUND
;
1114 #ifdef GIT_USE_ICONV
1115 if ((flags
& GIT_PATH_DIR_PRECOMPOSE_UNICODE
) != 0)
1116 (void)git_path_iconv_init_precompose(&ic
);
1119 while ((de
= readdir(dir
)) != NULL
) {
1120 const char *de_path
= de
->d_name
;
1121 size_t de_len
= strlen(de_path
);
1123 if (git_path_is_dot_or_dotdot(de_path
))
1126 #ifdef GIT_USE_ICONV
1127 if ((error
= git_path_iconv(&ic
, &de_path
, &de_len
)) < 0)
1131 if ((error
= git_buf_put(path
, de_path
, de_len
)) < 0)
1135 error
= fn(arg
, path
);
1137 git_buf_truncate(path
, wd_len
); /* restore path */
1139 /* Only set our own error if the callback did not set one already */
1141 if (!git_error_last())
1142 git_error_set_after_callback(error
);
1150 #ifdef GIT_USE_ICONV
1151 git_path_iconv_clear(&ic
);
1157 #if defined(GIT_WIN32) && !defined(__MINGW32__)
1159 /* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
1162 #ifndef FIND_FIRST_EX_LARGE_FETCH
1163 # define FIND_FIRST_EX_LARGE_FETCH 2
1166 int git_path_diriter_init(
1167 git_path_diriter
*diriter
,
1171 git_win32_path path_filter
;
1173 static int is_win7_or_later
= -1;
1174 if (is_win7_or_later
< 0)
1175 is_win7_or_later
= git_has_win32_version(6, 1, 0);
1177 assert(diriter
&& path
);
1179 memset(diriter
, 0, sizeof(git_path_diriter
));
1180 diriter
->handle
= INVALID_HANDLE_VALUE
;
1182 if (git_buf_puts(&diriter
->path_utf8
, path
) < 0)
1185 git_path_trim_slashes(&diriter
->path_utf8
);
1187 if (diriter
->path_utf8
.size
== 0) {
1188 git_error_set(GIT_ERROR_FILESYSTEM
, "could not open directory '%s'", path
);
1192 if ((diriter
->parent_len
= git_win32_path_from_utf8(diriter
->path
, diriter
->path_utf8
.ptr
)) < 0 ||
1193 !git_win32__findfirstfile_filter(path_filter
, diriter
->path_utf8
.ptr
)) {
1194 git_error_set(GIT_ERROR_OS
, "could not parse the directory path '%s'", path
);
1198 diriter
->handle
= FindFirstFileExW(
1200 is_win7_or_later
? FindExInfoBasic
: FindExInfoStandard
,
1202 FindExSearchNameMatch
,
1204 is_win7_or_later
? FIND_FIRST_EX_LARGE_FETCH
: 0);
1206 if (diriter
->handle
== INVALID_HANDLE_VALUE
) {
1207 git_error_set(GIT_ERROR_OS
, "could not open directory '%s'", path
);
1211 diriter
->parent_utf8_len
= diriter
->path_utf8
.size
;
1212 diriter
->flags
= flags
;
1216 static int diriter_update_paths(git_path_diriter
*diriter
)
1218 size_t filename_len
, path_len
;
1220 filename_len
= wcslen(diriter
->current
.cFileName
);
1222 if (GIT_ADD_SIZET_OVERFLOW(&path_len
, diriter
->parent_len
, filename_len
) ||
1223 GIT_ADD_SIZET_OVERFLOW(&path_len
, path_len
, 2))
1226 if (path_len
> GIT_WIN_PATH_UTF16
) {
1227 git_error_set(GIT_ERROR_FILESYSTEM
,
1228 "invalid path '%.*ls\\%ls' (path too long)",
1229 diriter
->parent_len
, diriter
->path
, diriter
->current
.cFileName
);
1233 diriter
->path
[diriter
->parent_len
] = L
'\\';
1234 memcpy(&diriter
->path
[diriter
->parent_len
+1],
1235 diriter
->current
.cFileName
, filename_len
* sizeof(wchar_t));
1236 diriter
->path
[path_len
-1] = L
'\0';
1238 git_buf_truncate(&diriter
->path_utf8
, diriter
->parent_utf8_len
);
1240 if (diriter
->parent_utf8_len
> 0 &&
1241 diriter
->path_utf8
.ptr
[diriter
->parent_utf8_len
-1] != '/')
1242 git_buf_putc(&diriter
->path_utf8
, '/');
1244 git_buf_put_w(&diriter
->path_utf8
, diriter
->current
.cFileName
, filename_len
);
1246 if (git_buf_oom(&diriter
->path_utf8
))
1252 int git_path_diriter_next(git_path_diriter
*diriter
)
1254 bool skip_dot
= !(diriter
->flags
& GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT
);
1257 /* Our first time through, we already have the data from
1258 * FindFirstFileW. Use it, otherwise get the next file.
1260 if (!diriter
->needs_next
)
1261 diriter
->needs_next
= 1;
1262 else if (!FindNextFileW(diriter
->handle
, &diriter
->current
))
1263 return GIT_ITEROVER
;
1264 } while (skip_dot
&& git_path_is_dot_or_dotdotW(diriter
->current
.cFileName
));
1266 if (diriter_update_paths(diriter
) < 0)
1272 int git_path_diriter_filename(
1275 git_path_diriter
*diriter
)
1277 assert(out
&& out_len
&& diriter
);
1279 assert(diriter
->path_utf8
.size
> diriter
->parent_utf8_len
);
1281 *out
= &diriter
->path_utf8
.ptr
[diriter
->parent_utf8_len
+1];
1282 *out_len
= diriter
->path_utf8
.size
- diriter
->parent_utf8_len
- 1;
1286 int git_path_diriter_fullpath(
1289 git_path_diriter
*diriter
)
1291 assert(out
&& out_len
&& diriter
);
1293 *out
= diriter
->path_utf8
.ptr
;
1294 *out_len
= diriter
->path_utf8
.size
;
1298 int git_path_diriter_stat(struct stat
*out
, git_path_diriter
*diriter
)
1300 assert(out
&& diriter
);
1302 return git_win32__file_attribute_to_stat(out
,
1303 (WIN32_FILE_ATTRIBUTE_DATA
*)&diriter
->current
,
1307 void git_path_diriter_free(git_path_diriter
*diriter
)
1309 if (diriter
== NULL
)
1312 git_buf_dispose(&diriter
->path_utf8
);
1314 if (diriter
->handle
!= INVALID_HANDLE_VALUE
) {
1315 FindClose(diriter
->handle
);
1316 diriter
->handle
= INVALID_HANDLE_VALUE
;
1322 int git_path_diriter_init(
1323 git_path_diriter
*diriter
,
1327 assert(diriter
&& path
);
1329 memset(diriter
, 0, sizeof(git_path_diriter
));
1331 if (git_buf_puts(&diriter
->path
, path
) < 0)
1334 git_path_trim_slashes(&diriter
->path
);
1336 if (diriter
->path
.size
== 0) {
1337 git_error_set(GIT_ERROR_FILESYSTEM
, "could not open directory '%s'", path
);
1341 if ((diriter
->dir
= opendir(diriter
->path
.ptr
)) == NULL
) {
1342 git_buf_dispose(&diriter
->path
);
1344 git_error_set(GIT_ERROR_OS
, "failed to open directory '%s'", path
);
1348 #ifdef GIT_USE_ICONV
1349 if ((flags
& GIT_PATH_DIR_PRECOMPOSE_UNICODE
) != 0)
1350 (void)git_path_iconv_init_precompose(&diriter
->ic
);
1353 diriter
->parent_len
= diriter
->path
.size
;
1354 diriter
->flags
= flags
;
1359 int git_path_diriter_next(git_path_diriter
*diriter
)
1362 const char *filename
;
1363 size_t filename_len
;
1364 bool skip_dot
= !(diriter
->flags
& GIT_PATH_DIR_INCLUDE_DOT_AND_DOTDOT
);
1372 if ((de
= readdir(diriter
->dir
)) == NULL
) {
1374 return GIT_ITEROVER
;
1376 git_error_set(GIT_ERROR_OS
,
1377 "could not read directory '%s'", diriter
->path
.ptr
);
1380 } while (skip_dot
&& git_path_is_dot_or_dotdot(de
->d_name
));
1382 filename
= de
->d_name
;
1383 filename_len
= strlen(filename
);
1385 #ifdef GIT_USE_ICONV
1386 if ((diriter
->flags
& GIT_PATH_DIR_PRECOMPOSE_UNICODE
) != 0 &&
1387 (error
= git_path_iconv(&diriter
->ic
, &filename
, &filename_len
)) < 0)
1391 git_buf_truncate(&diriter
->path
, diriter
->parent_len
);
1393 if (diriter
->parent_len
> 0 &&
1394 diriter
->path
.ptr
[diriter
->parent_len
-1] != '/')
1395 git_buf_putc(&diriter
->path
, '/');
1397 git_buf_put(&diriter
->path
, filename
, filename_len
);
1399 if (git_buf_oom(&diriter
->path
))
1405 int git_path_diriter_filename(
1408 git_path_diriter
*diriter
)
1410 assert(out
&& out_len
&& diriter
);
1412 assert(diriter
->path
.size
> diriter
->parent_len
);
1414 *out
= &diriter
->path
.ptr
[diriter
->parent_len
+1];
1415 *out_len
= diriter
->path
.size
- diriter
->parent_len
- 1;
1419 int git_path_diriter_fullpath(
1422 git_path_diriter
*diriter
)
1424 assert(out
&& out_len
&& diriter
);
1426 *out
= diriter
->path
.ptr
;
1427 *out_len
= diriter
->path
.size
;
1431 int git_path_diriter_stat(struct stat
*out
, git_path_diriter
*diriter
)
1433 assert(out
&& diriter
);
1435 return git_path_lstat(diriter
->path
.ptr
, out
);
1438 void git_path_diriter_free(git_path_diriter
*diriter
)
1440 if (diriter
== NULL
)
1444 closedir(diriter
->dir
);
1445 diriter
->dir
= NULL
;
1448 #ifdef GIT_USE_ICONV
1449 git_path_iconv_clear(&diriter
->ic
);
1452 git_buf_dispose(&diriter
->path
);
1457 int git_path_dirload(
1458 git_vector
*contents
,
1463 git_path_diriter iter
= GIT_PATH_DIRITER_INIT
;
1469 assert(contents
&& path
);
1471 if ((error
= git_path_diriter_init(&iter
, path
, flags
)) < 0)
1474 while ((error
= git_path_diriter_next(&iter
)) == 0) {
1475 if ((error
= git_path_diriter_fullpath(&name
, &name_len
, &iter
)) < 0)
1478 assert(name_len
> prefix_len
);
1480 dup
= git__strndup(name
+ prefix_len
, name_len
- prefix_len
);
1481 GIT_ERROR_CHECK_ALLOC(dup
);
1483 if ((error
= git_vector_insert(contents
, dup
)) < 0)
1487 if (error
== GIT_ITEROVER
)
1490 git_path_diriter_free(&iter
);
1494 int git_path_from_url_or_path(git_buf
*local_path_out
, const char *url_or_path
)
1496 if (git_path_is_local_file_url(url_or_path
))
1497 return git_path_fromurl(local_path_out
, url_or_path
);
1499 return git_buf_sets(local_path_out
, url_or_path
);
1502 /* Reject paths like AUX or COM1, or those versions that end in a dot or
1503 * colon. ("AUX." or "AUX:")
1505 GIT_INLINE(bool) verify_dospath(
1506 const char *component
,
1508 const char dospath
[3],
1511 size_t last
= trailing_num
? 4 : 3;
1513 if (len
< last
|| git__strncasecmp(component
, dospath
, 3) != 0)
1516 if (trailing_num
&& (component
[3] < '1' || component
[3] > '9'))
1519 return (len
> last
&&
1520 component
[last
] != '.' &&
1521 component
[last
] != ':');
1524 static int32_t next_hfs_char(const char **in
, size_t *len
)
1528 int cp_len
= git__utf8_iterate((const uint8_t *)(*in
), (int)(*len
), &codepoint
);
1535 /* these code points are ignored completely */
1536 switch (codepoint
) {
1537 case 0x200c: /* ZERO WIDTH NON-JOINER */
1538 case 0x200d: /* ZERO WIDTH JOINER */
1539 case 0x200e: /* LEFT-TO-RIGHT MARK */
1540 case 0x200f: /* RIGHT-TO-LEFT MARK */
1541 case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
1542 case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
1543 case 0x202c: /* POP DIRECTIONAL FORMATTING */
1544 case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
1545 case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
1546 case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
1547 case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
1548 case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
1549 case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
1550 case 0x206e: /* NATIONAL DIGIT SHAPES */
1551 case 0x206f: /* NOMINAL DIGIT SHAPES */
1552 case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
1556 /* fold into lowercase -- this will only fold characters in
1557 * the ASCII range, which is perfectly fine, because the
1558 * git folder name can only be composed of ascii characters
1560 return git__tolower(codepoint
);
1562 return 0; /* NULL byte -- end of string */
1565 static bool verify_dotgit_hfs_generic(const char *path
, size_t len
, const char *needle
, size_t needle_len
)
1570 if (next_hfs_char(&path
, &len
) != '.')
1573 for (i
= 0; i
< needle_len
; i
++) {
1574 c
= next_hfs_char(&path
, &len
);
1579 if (next_hfs_char(&path
, &len
) != '\0')
1585 static bool verify_dotgit_hfs(const char *path
, size_t len
)
1587 return verify_dotgit_hfs_generic(path
, len
, "git", CONST_STRLEN("git"));
1590 GIT_INLINE(bool) verify_dotgit_ntfs(git_repository
*repo
, const char *path
, size_t len
)
1592 git_buf
*reserved
= git_repository__reserved_names_win32
;
1593 size_t reserved_len
= git_repository__reserved_names_win32_len
;
1594 size_t start
= 0, i
;
1597 git_repository__reserved_names(&reserved
, &reserved_len
, repo
, true);
1599 for (i
= 0; i
< reserved_len
; i
++) {
1600 git_buf
*r
= &reserved
[i
];
1602 if (len
>= r
->size
&&
1603 strncasecmp(path
, r
->ptr
, r
->size
) == 0) {
1612 /* Reject paths like ".git\" */
1613 if (path
[start
] == '\\')
1616 /* Reject paths like '.git ' or '.git.' */
1617 for (i
= start
; i
< len
; i
++) {
1618 if (path
[i
] != ' ' && path
[i
] != '.')
1625 GIT_INLINE(bool) only_spaces_and_dots(const char *path
)
1627 const char *c
= path
;
1632 if (*c
!= ' ' && *c
!= '.')
1639 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
)
1643 if (name
[0] == '.' && len
>= dotgit_len
&&
1644 !strncasecmp(name
+ 1, dotgit_name
, dotgit_len
)) {
1645 return !only_spaces_and_dots(name
+ dotgit_len
+ 1);
1648 /* Detect the basic NTFS shortname with the first six chars */
1649 if (!strncasecmp(name
, dotgit_name
, 6) && name
[6] == '~' &&
1650 name
[7] >= '1' && name
[7] <= '4')
1651 return !only_spaces_and_dots(name
+ 8);
1653 /* Catch fallback names */
1654 for (i
= 0, saw_tilde
= 0; i
< 8; i
++) {
1655 if (name
[i
] == '\0') {
1657 } else if (saw_tilde
) {
1658 if (name
[i
] < '0' || name
[i
] > '9')
1660 } else if (name
[i
] == '~') {
1661 if (name
[i
+1] < '1' || name
[i
+1] > '9')
1664 } else if (i
>= 6) {
1666 } else if ((unsigned char)name
[i
] > 127) {
1668 } else if (git__tolower(name
[i
]) != shortname_pfix
[i
]) {
1673 return !only_spaces_and_dots(name
+ i
);
1676 GIT_INLINE(bool) verify_char(unsigned char c
, unsigned int flags
)
1678 if ((flags
& GIT_PATH_REJECT_BACKSLASH
) && c
== '\\')
1681 if ((flags
& GIT_PATH_REJECT_SLASH
) && c
== '/')
1684 if (flags
& GIT_PATH_REJECT_NT_CHARS
) {
1704 * Return the length of the common prefix between str and prefix, comparing them
1705 * case-insensitively (must be ASCII to match).
1707 GIT_INLINE(size_t) common_prefix_icase(const char *str
, size_t len
, const char *prefix
)
1711 while (len
>0 && tolower(*str
) == tolower(*prefix
)) {
1722 * We fundamentally don't like some paths when dealing with user-inputted
1723 * strings (in checkout or ref names): we don't want dot or dot-dot
1724 * anywhere, we want to avoid writing weird paths on Windows that can't
1725 * be handled by tools that use the non-\\?\ APIs, we don't want slashes
1726 * or double slashes at the end of paths that can make them ambiguous.
1728 * For checkout, we don't want to recurse into ".git" either.
1730 static bool verify_component(
1731 git_repository
*repo
,
1732 const char *component
,
1740 if ((flags
& GIT_PATH_REJECT_TRAVERSAL
) &&
1741 len
== 1 && component
[0] == '.')
1744 if ((flags
& GIT_PATH_REJECT_TRAVERSAL
) &&
1745 len
== 2 && component
[0] == '.' && component
[1] == '.')
1748 if ((flags
& GIT_PATH_REJECT_TRAILING_DOT
) && component
[len
-1] == '.')
1751 if ((flags
& GIT_PATH_REJECT_TRAILING_SPACE
) && component
[len
-1] == ' ')
1754 if ((flags
& GIT_PATH_REJECT_TRAILING_COLON
) && component
[len
-1] == ':')
1757 if (flags
& GIT_PATH_REJECT_DOS_PATHS
) {
1758 if (!verify_dospath(component
, len
, "CON", false) ||
1759 !verify_dospath(component
, len
, "PRN", false) ||
1760 !verify_dospath(component
, len
, "AUX", false) ||
1761 !verify_dospath(component
, len
, "NUL", false) ||
1762 !verify_dospath(component
, len
, "COM", true) ||
1763 !verify_dospath(component
, len
, "LPT", true))
1767 if (flags
& GIT_PATH_REJECT_DOT_GIT_HFS
) {
1768 if (!verify_dotgit_hfs(component
, len
))
1770 if (S_ISLNK(mode
) && git_path_is_gitfile(component
, len
, GIT_PATH_GITFILE_GITMODULES
, GIT_PATH_FS_HFS
))
1774 if (flags
& GIT_PATH_REJECT_DOT_GIT_NTFS
) {
1775 if (!verify_dotgit_ntfs(repo
, component
, len
))
1777 if (S_ISLNK(mode
) && git_path_is_gitfile(component
, len
, GIT_PATH_GITFILE_GITMODULES
, GIT_PATH_FS_NTFS
))
1781 /* don't bother rerunning the `.git` test if we ran the HFS or NTFS
1782 * specific tests, they would have already rejected `.git`.
1784 if ((flags
& GIT_PATH_REJECT_DOT_GIT_HFS
) == 0 &&
1785 (flags
& GIT_PATH_REJECT_DOT_GIT_NTFS
) == 0 &&
1786 (flags
& GIT_PATH_REJECT_DOT_GIT_LITERAL
)) {
1788 component
[0] == '.' &&
1789 (component
[1] == 'g' || component
[1] == 'G') &&
1790 (component
[2] == 'i' || component
[2] == 'I') &&
1791 (component
[3] == 't' || component
[3] == 'T')) {
1795 if (S_ISLNK(mode
) && common_prefix_icase(component
, len
, ".gitmodules") == len
)
1803 GIT_INLINE(unsigned int) dotgit_flags(
1804 git_repository
*repo
,
1807 int protectHFS
= 0, protectNTFS
= 0;
1810 flags
|= GIT_PATH_REJECT_DOT_GIT_LITERAL
;
1820 if (repo
&& !protectHFS
)
1821 error
= git_repository__cvar(&protectHFS
, repo
, GIT_CVAR_PROTECTHFS
);
1822 if (!error
&& protectHFS
)
1823 flags
|= GIT_PATH_REJECT_DOT_GIT_HFS
;
1825 if (repo
&& !protectNTFS
)
1826 error
= git_repository__cvar(&protectNTFS
, repo
, GIT_CVAR_PROTECTNTFS
);
1827 if (!error
&& protectNTFS
)
1828 flags
|= GIT_PATH_REJECT_DOT_GIT_NTFS
;
1833 bool git_path_isvalid(
1834 git_repository
*repo
,
1839 const char *start
, *c
;
1841 /* Upgrade the ".git" checks based on platform */
1842 if ((flags
& GIT_PATH_REJECT_DOT_GIT
))
1843 flags
= dotgit_flags(repo
, flags
);
1845 for (start
= c
= path
; *c
; c
++) {
1846 if (!verify_char(*c
, flags
))
1850 if (!verify_component(repo
, start
, (c
- start
), mode
, flags
))
1857 return verify_component(repo
, start
, (c
- start
), mode
, flags
);
1860 int git_path_normalize_slashes(git_buf
*out
, const char *path
)
1865 if ((error
= git_buf_puts(out
, path
)) < 0)
1868 for (p
= out
->ptr
; *p
; p
++) {
1876 static const struct {
1881 { "gitignore", "gi250a", CONST_STRLEN("gitignore") },
1882 { "gitmodules", "gi7eba", CONST_STRLEN("gitmodules") },
1883 { "gitattributes", "gi7d29", CONST_STRLEN("gitattributes") }
1886 extern int git_path_is_gitfile(const char *path
, size_t pathlen
, git_path_gitfile gitfile
, git_path_fs fs
)
1888 const char *file
, *hash
;
1891 if (!(gitfile
>= GIT_PATH_GITFILE_GITIGNORE
&& gitfile
< ARRAY_SIZE(gitfiles
))) {
1892 git_error_set(GIT_ERROR_OS
, "invalid gitfile for path validation");
1896 file
= gitfiles
[gitfile
].file
;
1897 filelen
= gitfiles
[gitfile
].filelen
;
1898 hash
= gitfiles
[gitfile
].hash
;
1901 case GIT_PATH_FS_GENERIC
:
1902 return !verify_dotgit_ntfs_generic(path
, pathlen
, file
, filelen
, hash
) ||
1903 !verify_dotgit_hfs_generic(path
, pathlen
, file
, filelen
);
1904 case GIT_PATH_FS_NTFS
:
1905 return !verify_dotgit_ntfs_generic(path
, pathlen
, file
, filelen
, hash
);
1906 case GIT_PATH_FS_HFS
:
1907 return !verify_dotgit_hfs_generic(path
, pathlen
, file
, filelen
);
1909 git_error_set(GIT_ERROR_OS
, "invalid filesystem for path validation");
1914 int git_path_validate_system_file_ownership(const char *path
)
1922 PSECURITY_DESCRIPTOR descriptor
= NULL
;
1924 TOKEN_USER
*info
= NULL
;
1928 if (git_win32_path_from_utf8(buf
, path
) < 0)
1931 err
= GetNamedSecurityInfoW(buf
, SE_FILE_OBJECT
,
1932 OWNER_SECURITY_INFORMATION
|
1933 DACL_SECURITY_INFORMATION
,
1934 &owner_sid
, NULL
, NULL
, NULL
, &descriptor
);
1936 if (err
== ERROR_FILE_NOT_FOUND
|| err
== ERROR_PATH_NOT_FOUND
) {
1937 ret
= GIT_ENOTFOUND
;
1941 if (err
!= ERROR_SUCCESS
) {
1942 git_error_set(GIT_ERROR_OS
, "failed to get security information");
1947 if (!IsValidSid(owner_sid
)) {
1948 git_error_set(GIT_ERROR_INVALID
, "programdata configuration file owner is unknown");
1953 if (IsWellKnownSid(owner_sid
, WinBuiltinAdministratorsSid
) ||
1954 IsWellKnownSid(owner_sid
, WinLocalSystemSid
)) {
1959 /* Obtain current user's SID */
1960 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY
, &token
) &&
1961 !GetTokenInformation(token
, TokenUser
, NULL
, 0, &len
)) {
1962 info
= git__malloc(len
);
1963 GIT_ERROR_CHECK_ALLOC(info
);
1964 if (!GetTokenInformation(token
, TokenUser
, info
, len
, &len
)) {
1971 * If the file is owned by the same account that is running the current
1972 * process, it's okay to read from that file.
1974 if (info
&& EqualSid(owner_sid
, info
->User
.Sid
))
1977 git_error_set(GIT_ERROR_INVALID
, "programdata configuration file owner is not valid");
1984 LocalFree(descriptor
);