rugged 0.21.2 → 0.21.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -441,4 +441,47 @@ extern bool git_path_does_fs_decompose_unicode(const char *root);
441
441
  /* Used for paths to repositories on the filesystem */
442
442
  extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
443
443
 
444
+ /* Flags to determine path validity in `git_path_isvalid` */
445
+ #define GIT_PATH_REJECT_TRAVERSAL (1 << 0)
446
+ #define GIT_PATH_REJECT_DOT_GIT (1 << 1)
447
+ #define GIT_PATH_REJECT_SLASH (1 << 2)
448
+ #define GIT_PATH_REJECT_BACKSLASH (1 << 3)
449
+ #define GIT_PATH_REJECT_TRAILING_DOT (1 << 4)
450
+ #define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5)
451
+ #define GIT_PATH_REJECT_TRAILING_COLON (1 << 6)
452
+ #define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
453
+ #define GIT_PATH_REJECT_NT_CHARS (1 << 8)
454
+ #define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9)
455
+ #define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10)
456
+
457
+ /* Default path safety for writing files to disk: since we use the
458
+ * Win32 "File Namespace" APIs ("\\?\") we need to protect from
459
+ * paths that the normal Win32 APIs would not write.
460
+ */
461
+ #ifdef GIT_WIN32
462
+ # define GIT_PATH_REJECT_DEFAULTS \
463
+ GIT_PATH_REJECT_TRAVERSAL | \
464
+ GIT_PATH_REJECT_BACKSLASH | \
465
+ GIT_PATH_REJECT_TRAILING_DOT | \
466
+ GIT_PATH_REJECT_TRAILING_SPACE | \
467
+ GIT_PATH_REJECT_TRAILING_COLON | \
468
+ GIT_PATH_REJECT_DOS_PATHS | \
469
+ GIT_PATH_REJECT_NT_CHARS
470
+ #else
471
+ # define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL
472
+ #endif
473
+
474
+ /*
475
+ * Determine whether a path is a valid git path or not - this must not contain
476
+ * a '.' or '..' component, or a component that is ".git" (in any case).
477
+ *
478
+ * `repo` is optional. If specified, it will be used to determine the short
479
+ * path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified),
480
+ * in addition to the default of "git~1".
481
+ */
482
+ extern bool git_path_isvalid(
483
+ git_repository *repo,
484
+ const char *path,
485
+ unsigned int flags);
486
+
444
487
  #endif
@@ -707,11 +707,16 @@ static int reference_path_available(
707
707
 
708
708
  static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
709
709
  {
710
- int error;
710
+ int error;
711
711
  git_buf ref_path = GIT_BUF_INIT;
712
712
 
713
713
  assert(file && backend && name);
714
714
 
715
+ if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_DEFAULTS)) {
716
+ giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name);
717
+ return GIT_EINVALIDSPEC;
718
+ }
719
+
715
720
  /* Remove a possibly existing empty directory hierarchy
716
721
  * which name would collide with the reference name
717
722
  */
@@ -1573,6 +1578,11 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char
1573
1578
 
1574
1579
  repo = backend->repo;
1575
1580
 
1581
+ if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_DEFAULTS)) {
1582
+ giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname);
1583
+ return GIT_EINVALIDSPEC;
1584
+ }
1585
+
1576
1586
  if (retrieve_reflog_path(&log_path, repo, refname) < 0)
1577
1587
  return -1;
1578
1588
 
@@ -37,6 +37,9 @@
37
37
 
38
38
  #define GIT_REPO_VERSION 0
39
39
 
40
+ const char *git_repository__8dot3_default = "GIT~1";
41
+ size_t git_repository__8dot3_default_len = 5;
42
+
40
43
  static void set_odb(git_repository *repo, git_odb *odb)
41
44
  {
42
45
  if (odb) {
@@ -120,6 +123,7 @@ void git_repository_free(git_repository *repo)
120
123
  git__free(repo->path_repository);
121
124
  git__free(repo->workdir);
122
125
  git__free(repo->namespace);
126
+ git__free(repo->name_8dot3);
123
127
 
124
128
  git__memzero(repo, sizeof(*repo));
125
129
  git__free(repo);
@@ -791,6 +795,27 @@ const char *git_repository_get_namespace(git_repository *repo)
791
795
  return repo->namespace;
792
796
  }
793
797
 
798
+ const char *git_repository__8dot3_name(git_repository *repo)
799
+ {
800
+ if (!repo->has_8dot3) {
801
+ repo->has_8dot3 = 1;
802
+
803
+ #ifdef GIT_WIN32
804
+ if (!repo->is_bare) {
805
+ repo->name_8dot3 = git_win32_path_8dot3_name(repo->path_repository);
806
+
807
+ /* We anticipate the 8.3 name is "GIT~1", so use a static for
808
+ * easy testing in the common case */
809
+ if (strcasecmp(repo->name_8dot3, git_repository__8dot3_default) == 0)
810
+ repo->has_8dot3_default = 1;
811
+ }
812
+ #endif
813
+ }
814
+
815
+ return repo->has_8dot3_default ?
816
+ git_repository__8dot3_default : repo->name_8dot3;
817
+ }
818
+
794
819
  static int check_repositoryformatversion(git_config *config)
795
820
  {
796
821
  int version;
@@ -40,6 +40,8 @@ typedef enum {
40
40
  GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
41
41
  GIT_CVAR_SAFE_CRLF, /* core.safecrlf */
42
42
  GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
43
+ GIT_CVAR_PROTECTHFS, /* core.protectHFS */
44
+ GIT_CVAR_PROTECTNTFS, /* core.protectNTFS */
43
45
  GIT_CVAR_CACHE_MAX
44
46
  } git_cvar_cached;
45
47
 
@@ -96,6 +98,10 @@ typedef enum {
96
98
  /* core.logallrefupdates */
97
99
  GIT_LOGALLREFUPDATES_UNSET = 2,
98
100
  GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
101
+ /* core.protectHFS */
102
+ GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE,
103
+ /* core.protectNTFS */
104
+ GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE,
99
105
  } git_cvar_value;
100
106
 
101
107
  /* internal repository init flags */
@@ -120,8 +126,11 @@ struct git_repository {
120
126
  char *path_repository;
121
127
  char *workdir;
122
128
  char *namespace;
129
+ char *name_8dot3;
123
130
 
124
- unsigned is_bare:1;
131
+ unsigned is_bare:1,
132
+ has_8dot3:1,
133
+ has_8dot3_default:1;
125
134
  unsigned int lru_counter;
126
135
 
127
136
  git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
@@ -172,4 +181,19 @@ GIT_INLINE(int) git_repository__ensure_not_bare(
172
181
 
173
182
  int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
174
183
 
184
+ /*
185
+ * Gets the DOS-compatible 8.3 "short name". This will return only the
186
+ * short name for the repository directory (ie, "git~1" for ".git"). This
187
+ * will always return a pointer to `git_repository__8dot3_default` when
188
+ * "GIT~1" is the short name. This will return NULL for bare repositories,
189
+ * and systems that do not have a short name.
190
+ */
191
+ const char *git_repository__8dot3_name(git_repository *repo);
192
+
193
+ /* The default DOS-compatible 8.3 "short name" for a git repository,
194
+ * "GIT~1".
195
+ */
196
+ extern const char *git_repository__8dot3_default;
197
+ extern size_t git_repository__8dot3_default_len;
198
+
175
199
  #endif
@@ -55,7 +55,7 @@ static int valid_entry_name(const char *filename)
55
55
  (*filename != '.' ||
56
56
  (strcmp(filename, ".") != 0 &&
57
57
  strcmp(filename, "..") != 0 &&
58
- strcmp(filename, DOT_GIT) != 0));
58
+ strcasecmp(filename, DOT_GIT) != 0));
59
59
  }
60
60
 
61
61
  static int entry_sort_cmp(const void *a, const void *b)
@@ -250,6 +250,21 @@ int git__prefixcmp_icase(const char *str, const char *prefix)
250
250
  return strncasecmp(str, prefix, strlen(prefix));
251
251
  }
252
252
 
253
+ int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
254
+ {
255
+ int s, p;
256
+
257
+ while(str_n--) {
258
+ s = (unsigned char)tolower(*str++);
259
+ p = (unsigned char)tolower(*prefix++);
260
+
261
+ if (s != p)
262
+ return s - p;
263
+ }
264
+
265
+ return (0 - *prefix);
266
+ }
267
+
253
268
  int git__suffixcmp(const char *str, const char *suffix)
254
269
  {
255
270
  size_t a = strlen(str);
@@ -648,3 +663,79 @@ void git__insertsort_r(
648
663
  if (freeswap)
649
664
  git__free(swapel);
650
665
  }
666
+
667
+ static const int8_t utf8proc_utf8class[256] = {
668
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
669
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
670
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
671
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
672
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
673
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
674
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
675
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
676
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
677
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
678
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
679
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
680
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
681
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
682
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
683
+ 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
684
+ };
685
+
686
+ int git__utf8_charlen(const uint8_t *str, int str_len)
687
+ {
688
+ int length, i;
689
+
690
+ length = utf8proc_utf8class[str[0]];
691
+ if (!length)
692
+ return -1;
693
+
694
+ if (str_len >= 0 && length > str_len)
695
+ return -str_len;
696
+
697
+ for (i = 1; i < length; i++) {
698
+ if ((str[i] & 0xC0) != 0x80)
699
+ return -i;
700
+ }
701
+
702
+ return length;
703
+ }
704
+
705
+ int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst)
706
+ {
707
+ int length;
708
+ int32_t uc = -1;
709
+
710
+ *dst = -1;
711
+ length = git__utf8_charlen(str, str_len);
712
+ if (length < 0)
713
+ return -1;
714
+
715
+ switch (length) {
716
+ case 1:
717
+ uc = str[0];
718
+ break;
719
+ case 2:
720
+ uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
721
+ if (uc < 0x80) uc = -1;
722
+ break;
723
+ case 3:
724
+ uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
725
+ + (str[2] & 0x3F);
726
+ if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
727
+ (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
728
+ break;
729
+ case 4:
730
+ uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
731
+ + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
732
+ if (uc < 0x10000 || uc >= 0x110000) uc = -1;
733
+ break;
734
+ }
735
+
736
+ if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
737
+ return -1;
738
+
739
+ *dst = uc;
740
+ return length;
741
+ }
@@ -106,6 +106,7 @@ GIT_INLINE(void) git__free(void *ptr)
106
106
 
107
107
  extern int git__prefixcmp(const char *str, const char *prefix);
108
108
  extern int git__prefixcmp_icase(const char *str, const char *prefix);
109
+ extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
109
110
  extern int git__suffixcmp(const char *str, const char *suffix);
110
111
 
111
112
  GIT_INLINE(int) git__signum(int val)
@@ -366,6 +367,17 @@ extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date);
366
367
  */
367
368
  extern size_t git__unescape(char *str);
368
369
 
370
+ /*
371
+ * Iterate through an UTF-8 string, yielding one
372
+ * codepoint at a time.
373
+ *
374
+ * @param str current position in the string
375
+ * @param str_len size left in the string; -1 if the string is NULL-terminated
376
+ * @param dst pointer where to store the current codepoint
377
+ * @return length in bytes of the read codepoint; -1 if the codepoint was invalid
378
+ */
379
+ extern int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst);
380
+
369
381
  /*
370
382
  * Safely zero-out memory, making sure that the compiler
371
383
  * doesn't optimize away the operation.
@@ -5,6 +5,7 @@
5
5
  * a Linking Exception. For full terms see the included COPYING file.
6
6
  */
7
7
 
8
+ #include "path_w32.h"
8
9
  #include "utf-conv.h"
9
10
  #include "path.h"
10
11
  #include "findfile.h"
@@ -0,0 +1,305 @@
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 "common.h"
9
+ #include "path.h"
10
+ #include "path_w32.h"
11
+ #include "utf-conv.h"
12
+
13
+ #define PATH__NT_NAMESPACE L"\\\\?\\"
14
+ #define PATH__NT_NAMESPACE_LEN 4
15
+
16
+ #define PATH__ABSOLUTE_LEN 3
17
+
18
+ #define path__is_dirsep(p) ((p) == '/' || (p) == '\\')
19
+
20
+ #define path__is_absolute(p) \
21
+ (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
22
+
23
+ #define path__is_nt_namespace(p) \
24
+ (((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
25
+ ((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
26
+
27
+ #define path__is_unc(p) \
28
+ (((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
29
+
30
+ GIT_INLINE(int) path__cwd(wchar_t *path, int size)
31
+ {
32
+ int len;
33
+
34
+ if ((len = GetCurrentDirectoryW(size, path)) == 0) {
35
+ errno = GetLastError() == ERROR_ACCESS_DENIED ? EACCES : ENOENT;
36
+ return -1;
37
+ } else if (len > size) {
38
+ errno = ENAMETOOLONG;
39
+ return -1;
40
+ }
41
+
42
+ /* The Win32 APIs may return "\\?\" once you've used it first.
43
+ * But it may not. What a gloriously predictible API!
44
+ */
45
+ if (wcsncmp(path, PATH__NT_NAMESPACE, PATH__NT_NAMESPACE_LEN))
46
+ return len;
47
+
48
+ len -= PATH__NT_NAMESPACE_LEN;
49
+
50
+ memmove(path, path + PATH__NT_NAMESPACE_LEN, sizeof(wchar_t) * len);
51
+ return len;
52
+ }
53
+
54
+ static wchar_t *path__skip_server(wchar_t *path)
55
+ {
56
+ wchar_t *c;
57
+
58
+ for (c = path; *c; c++) {
59
+ if (path__is_dirsep(*c))
60
+ return c + 1;
61
+ }
62
+
63
+ return c;
64
+ }
65
+
66
+ static wchar_t *path__skip_prefix(wchar_t *path)
67
+ {
68
+ if (path__is_nt_namespace(path)) {
69
+ path += PATH__NT_NAMESPACE_LEN;
70
+
71
+ if (wcsncmp(path, L"UNC\\", 4) == 0)
72
+ path = path__skip_server(path + 4);
73
+ else if (path__is_absolute(path))
74
+ path += PATH__ABSOLUTE_LEN;
75
+ } else if (path__is_absolute(path)) {
76
+ path += PATH__ABSOLUTE_LEN;
77
+ } else if (path__is_unc(path)) {
78
+ path = path__skip_server(path + 2);
79
+ }
80
+
81
+ return path;
82
+ }
83
+
84
+ int git_win32_path_canonicalize(git_win32_path path)
85
+ {
86
+ wchar_t *base, *from, *to, *next;
87
+ size_t len;
88
+
89
+ base = to = path__skip_prefix(path);
90
+
91
+ /* Unposixify if the prefix */
92
+ for (from = path; from < to; from++) {
93
+ if (*from == L'/')
94
+ *from = L'\\';
95
+ }
96
+
97
+ while (*from) {
98
+ for (next = from; *next; ++next) {
99
+ if (*next == L'/') {
100
+ *next = L'\\';
101
+ break;
102
+ }
103
+
104
+ if (*next == L'\\')
105
+ break;
106
+ }
107
+
108
+ len = next - from;
109
+
110
+ if (len == 1 && from[0] == L'.')
111
+ /* do nothing with singleton dot */;
112
+
113
+ else if (len == 2 && from[0] == L'.' && from[1] == L'.') {
114
+ if (to == base) {
115
+ /* no more path segments to strip, eat the "../" */
116
+ if (*next == L'\\')
117
+ len++;
118
+
119
+ base = to;
120
+ } else {
121
+ /* back up a path segment */
122
+ while (to > base && to[-1] == L'\\') to--;
123
+ while (to > base && to[-1] != L'\\') to--;
124
+ }
125
+ } else {
126
+ if (*next == L'\\' && *from != L'\\')
127
+ len++;
128
+
129
+ if (to != from)
130
+ memmove(to, from, sizeof(wchar_t) * len);
131
+
132
+ to += len;
133
+ }
134
+
135
+ from += len;
136
+
137
+ while (*from == L'\\') from++;
138
+ }
139
+
140
+ /* Strip trailing backslashes */
141
+ while (to > base && to[-1] == L'\\') to--;
142
+
143
+ *to = L'\0';
144
+
145
+ return (to - path);
146
+ }
147
+
148
+ int git_win32_path__cwd(wchar_t *out, size_t len)
149
+ {
150
+ int cwd_len;
151
+
152
+ if ((cwd_len = path__cwd(out, len)) < 0)
153
+ return -1;
154
+
155
+ /* UNC paths */
156
+ if (wcsncmp(L"\\\\", out, 2) == 0) {
157
+ /* Our buffer must be at least 5 characters larger than the
158
+ * current working directory: we swallow one of the leading
159
+ * '\'s, but we we add a 'UNC' specifier to the path, plus
160
+ * a trailing directory separator, plus a NUL.
161
+ */
162
+ if (cwd_len > MAX_PATH - 4) {
163
+ errno = ENAMETOOLONG;
164
+ return -1;
165
+ }
166
+
167
+ memmove(out+2, out, sizeof(wchar_t) * cwd_len);
168
+ out[0] = L'U';
169
+ out[1] = L'N';
170
+ out[2] = L'C';
171
+
172
+ cwd_len += 2;
173
+ }
174
+
175
+ /* Our buffer must be at least 2 characters larger than the current
176
+ * working directory. (One character for the directory separator,
177
+ * one for the null.
178
+ */
179
+ else if (cwd_len > MAX_PATH - 2) {
180
+ errno = ENAMETOOLONG;
181
+ return -1;
182
+ }
183
+
184
+ return cwd_len;
185
+ }
186
+
187
+ int git_win32_path_from_utf8(git_win32_path out, const char *src)
188
+ {
189
+ wchar_t *dest = out;
190
+
191
+ /* All win32 paths are in NT-prefixed format, beginning with "\\?\". */
192
+ memcpy(dest, PATH__NT_NAMESPACE, sizeof(wchar_t) * PATH__NT_NAMESPACE_LEN);
193
+ dest += PATH__NT_NAMESPACE_LEN;
194
+
195
+ /* See if this is an absolute path (beginning with a drive letter) */
196
+ if (path__is_absolute(src)) {
197
+ if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
198
+ return -1;
199
+ }
200
+ /* File-prefixed NT-style paths beginning with \\?\ */
201
+ else if (path__is_nt_namespace(src)) {
202
+ /* Skip the NT prefix, the destination already contains it */
203
+ if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0)
204
+ return -1;
205
+ }
206
+ /* UNC paths */
207
+ else if (path__is_unc(src)) {
208
+ memcpy(dest, L"UNC\\", sizeof(wchar_t) * 4);
209
+ dest += 4;
210
+
211
+ /* Skip the leading "\\" */
212
+ if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0)
213
+ return -1;
214
+ }
215
+ /* Absolute paths omitting the drive letter */
216
+ else if (src[0] == '\\' || src[0] == '/') {
217
+ if (path__cwd(dest, MAX_PATH) < 0)
218
+ return -1;
219
+
220
+ if (!path__is_absolute(dest)) {
221
+ errno = ENOENT;
222
+ return -1;
223
+ }
224
+
225
+ /* Skip the drive letter specification ("C:") */
226
+ if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0)
227
+ return -1;
228
+ }
229
+ /* Relative paths */
230
+ else {
231
+ int cwd_len;
232
+
233
+ if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0)
234
+ return -1;
235
+
236
+ dest[cwd_len++] = L'\\';
237
+
238
+ if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0)
239
+ return -1;
240
+ }
241
+
242
+ return git_win32_path_canonicalize(out);
243
+ }
244
+
245
+ int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
246
+ {
247
+ char *out = dest;
248
+ int len;
249
+
250
+ /* Strip NT namespacing "\\?\" */
251
+ if (path__is_nt_namespace(src)) {
252
+ src += 4;
253
+
254
+ /* "\\?\UNC\server\share" -> "\\server\share" */
255
+ if (wcsncmp(src, L"UNC\\", 4) == 0) {
256
+ src += 4;
257
+
258
+ memcpy(dest, "\\\\", 2);
259
+ out = dest + 2;
260
+ }
261
+ }
262
+
263
+ if ((len = git__utf16_to_8(out, GIT_WIN_PATH_UTF8, src)) < 0)
264
+ return len;
265
+
266
+ git_path_mkposix(dest);
267
+
268
+ return len;
269
+ }
270
+
271
+ char *git_win32_path_8dot3_name(const char *path)
272
+ {
273
+ git_win32_path longpath, shortpath;
274
+ wchar_t *start;
275
+ char *shortname;
276
+ int len, namelen = 1;
277
+
278
+ if (git_win32_path_from_utf8(longpath, path) < 0)
279
+ return NULL;
280
+
281
+ len = GetShortPathNameW(longpath, shortpath, GIT_WIN_PATH_UTF16);
282
+
283
+ while (len && shortpath[len-1] == L'\\')
284
+ shortpath[--len] = L'\0';
285
+
286
+ if (len == 0 || len >= GIT_WIN_PATH_UTF16)
287
+ return NULL;
288
+
289
+ for (start = shortpath + (len - 1);
290
+ start > shortpath && *(start-1) != '/' && *(start-1) != '\\';
291
+ start--)
292
+ namelen++;
293
+
294
+ /* We may not have actually been given a short name. But if we have,
295
+ * it will be in the ASCII byte range, so we don't need to worry about
296
+ * multi-byte sequences and can allocate naively.
297
+ */
298
+ if (namelen > 12 || (shortname = git__malloc(namelen + 1)) == NULL)
299
+ return NULL;
300
+
301
+ if ((len = git__utf16_to_8(shortname, namelen + 1, start)) < 0)
302
+ return NULL;
303
+
304
+ return shortname;
305
+ }