rugged 0.21.2 → 0.21.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }