Skip to content

Commit 85faec9

Browse files
kbleesgitster
authored andcommitted
Win32: Unicode file name support (except dirent)
Replaces Windows "ANSI" APIs dealing with file- or path names with their Unicode equivalent, adding UTF-8/UTF-16LE conversion as necessary. The dirent API (opendir/readdir/closedir) is updated in a separate commit. Adds trivial wrappers for access, chmod and chdir. Adds wrapper for mktemp (needed for both mkstemp and mkdtemp). The simplest way to convert a repository with legacy-encoded (e.g. Cp1252) file names to UTF-8 ist to checkout with an old msysgit version and "git add --all & git commit" with the new version. Includes a fix for bug reported by John Chen: On Windows XP (not Win7), directories cannot be deleted while a find handle is open, causing "Deletion of directory '...' failed. Should I try again?" prompts. Prior to this commit, these failures were silently ignored due to strbuf_free in is_dir_empty resetting GetLastError to ERROR_SUCCESS. Close the find handle in is_dir_empty so that git doesn't block deletion of the directory even after all other applications have released it. Reported-by: John Chen <[email protected]> Signed-off-by: Karsten Blees <[email protected]> Signed-off-by: Stepan Kasal <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 66f467c commit 85faec9

File tree

2 files changed

+160
-56
lines changed

2 files changed

+160
-56
lines changed

compat/mingw.c

Lines changed: 146 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "../git-compat-util.h"
22
#include "win32.h"
33
#include <conio.h>
4+
#include <wchar.h>
45
#include "../strbuf.h"
56
#include "../run-command.h"
67

@@ -198,14 +199,16 @@ static int ask_yes_no_if_possible(const char *format, ...)
198199
}
199200
}
200201

201-
#undef unlink
202202
int mingw_unlink(const char *pathname)
203203
{
204204
int ret, tries = 0;
205+
wchar_t wpathname[MAX_PATH];
206+
if (xutftowcs_path(wpathname, pathname) < 0)
207+
return -1;
205208

206209
/* read-only files cannot be removed */
207-
chmod(pathname, 0666);
208-
while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
210+
_wchmod(wpathname, 0666);
211+
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
209212
if (!is_file_in_use_error(GetLastError()))
210213
break;
211214
/*
@@ -221,45 +224,45 @@ int mingw_unlink(const char *pathname)
221224
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
222225
ask_yes_no_if_possible("Unlink of file '%s' failed. "
223226
"Should I try again?", pathname))
224-
ret = unlink(pathname);
227+
ret = _wunlink(wpathname);
225228
return ret;
226229
}
227230

228-
static int is_dir_empty(const char *path)
231+
static int is_dir_empty(const wchar_t *wpath)
229232
{
230-
struct strbuf buf = STRBUF_INIT;
231-
WIN32_FIND_DATAA findbuf;
233+
WIN32_FIND_DATAW findbuf;
232234
HANDLE handle;
233-
234-
strbuf_addf(&buf, "%s\\*", path);
235-
handle = FindFirstFileA(buf.buf, &findbuf);
236-
if (handle == INVALID_HANDLE_VALUE) {
237-
strbuf_release(&buf);
235+
wchar_t wbuf[MAX_PATH + 2];
236+
wcscpy(wbuf, wpath);
237+
wcscat(wbuf, L"\\*");
238+
handle = FindFirstFileW(wbuf, &findbuf);
239+
if (handle == INVALID_HANDLE_VALUE)
238240
return GetLastError() == ERROR_NO_MORE_FILES;
239-
}
240241

241-
while (!strcmp(findbuf.cFileName, ".") ||
242-
!strcmp(findbuf.cFileName, ".."))
243-
if (!FindNextFile(handle, &findbuf)) {
244-
strbuf_release(&buf);
245-
return GetLastError() == ERROR_NO_MORE_FILES;
242+
while (!wcscmp(findbuf.cFileName, L".") ||
243+
!wcscmp(findbuf.cFileName, L".."))
244+
if (!FindNextFileW(handle, &findbuf)) {
245+
DWORD err = GetLastError();
246+
FindClose(handle);
247+
return err == ERROR_NO_MORE_FILES;
246248
}
247249
FindClose(handle);
248-
strbuf_release(&buf);
249250
return 0;
250251
}
251252

252-
#undef rmdir
253253
int mingw_rmdir(const char *pathname)
254254
{
255255
int ret, tries = 0;
256+
wchar_t wpathname[MAX_PATH];
257+
if (xutftowcs_path(wpathname, pathname) < 0)
258+
return -1;
256259

257-
while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
260+
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
258261
if (!is_file_in_use_error(GetLastError()))
259262
errno = err_win_to_posix(GetLastError());
260263
if (errno != EACCES)
261264
break;
262-
if (!is_dir_empty(pathname)) {
265+
if (!is_dir_empty(wpathname)) {
263266
errno = ENOTEMPTY;
264267
break;
265268
}
@@ -276,16 +279,26 @@ int mingw_rmdir(const char *pathname)
276279
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
277280
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
278281
"Should I try again?", pathname))
279-
ret = rmdir(pathname);
282+
ret = _wrmdir(wpathname);
283+
return ret;
284+
}
285+
286+
int mingw_mkdir(const char *path, int mode)
287+
{
288+
int ret;
289+
wchar_t wpath[MAX_PATH];
290+
if (xutftowcs_path(wpath, path) < 0)
291+
return -1;
292+
ret = _wmkdir(wpath);
280293
return ret;
281294
}
282295

283-
#undef open
284296
int mingw_open (const char *filename, int oflags, ...)
285297
{
286298
va_list args;
287299
unsigned mode;
288300
int fd;
301+
wchar_t wfilename[MAX_PATH];
289302

290303
va_start(args, oflags);
291304
mode = va_arg(args, int);
@@ -294,10 +307,12 @@ int mingw_open (const char *filename, int oflags, ...)
294307
if (filename && !strcmp(filename, "/dev/null"))
295308
filename = "nul";
296309

297-
fd = open(filename, oflags, mode);
310+
if (xutftowcs_path(wfilename, filename) < 0)
311+
return -1;
312+
fd = _wopen(wfilename, oflags, mode);
298313

299314
if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
300-
DWORD attrs = GetFileAttributes(filename);
315+
DWORD attrs = GetFileAttributesW(wfilename);
301316
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
302317
errno = EISDIR;
303318
}
@@ -332,17 +347,28 @@ int mingw_fgetc(FILE *stream)
332347
#undef fopen
333348
FILE *mingw_fopen (const char *filename, const char *otype)
334349
{
350+
FILE *file;
351+
wchar_t wfilename[MAX_PATH], wotype[4];
335352
if (filename && !strcmp(filename, "/dev/null"))
336353
filename = "nul";
337-
return fopen(filename, otype);
354+
if (xutftowcs_path(wfilename, filename) < 0 ||
355+
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
356+
return NULL;
357+
file = _wfopen(wfilename, wotype);
358+
return file;
338359
}
339360

340-
#undef freopen
341361
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
342362
{
363+
FILE *file;
364+
wchar_t wfilename[MAX_PATH], wotype[4];
343365
if (filename && !strcmp(filename, "/dev/null"))
344366
filename = "nul";
345-
return freopen(filename, otype, stream);
367+
if (xutftowcs_path(wfilename, filename) < 0 ||
368+
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
369+
return NULL;
370+
file = _wfreopen(wfilename, wotype, stream);
371+
return file;
346372
}
347373

348374
#undef fflush
@@ -367,6 +393,31 @@ int mingw_fflush(FILE *stream)
367393
return ret;
368394
}
369395

396+
int mingw_access(const char *filename, int mode)
397+
{
398+
wchar_t wfilename[MAX_PATH];
399+
if (xutftowcs_path(wfilename, filename) < 0)
400+
return -1;
401+
/* X_OK is not supported by the MSVCRT version */
402+
return _waccess(wfilename, mode & ~X_OK);
403+
}
404+
405+
int mingw_chdir(const char *dirname)
406+
{
407+
wchar_t wdirname[MAX_PATH];
408+
if (xutftowcs_path(wdirname, dirname) < 0)
409+
return -1;
410+
return _wchdir(wdirname);
411+
}
412+
413+
int mingw_chmod(const char *filename, int mode)
414+
{
415+
wchar_t wfilename[MAX_PATH];
416+
if (xutftowcs_path(wfilename, filename) < 0)
417+
return -1;
418+
return _wchmod(wfilename, mode);
419+
}
420+
370421
/*
371422
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
372423
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
@@ -392,10 +443,12 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
392443
*/
393444
static int do_lstat(int follow, const char *file_name, struct stat *buf)
394445
{
395-
int err;
396446
WIN32_FILE_ATTRIBUTE_DATA fdata;
447+
wchar_t wfilename[MAX_PATH];
448+
if (xutftowcs_path(wfilename, file_name) < 0)
449+
return -1;
397450

398-
if (!(err = get_file_attr(file_name, &fdata))) {
451+
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
399452
buf->st_ino = 0;
400453
buf->st_gid = 0;
401454
buf->st_uid = 0;
@@ -408,8 +461,8 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
408461
buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
409462
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
410463
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
411-
WIN32_FIND_DATAA findbuf;
412-
HANDLE handle = FindFirstFileA(file_name, &findbuf);
464+
WIN32_FIND_DATAW findbuf;
465+
HANDLE handle = FindFirstFileW(wfilename, &findbuf);
413466
if (handle != INVALID_HANDLE_VALUE) {
414467
if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
415468
(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
@@ -428,7 +481,23 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
428481
}
429482
return 0;
430483
}
431-
errno = err;
484+
switch (GetLastError()) {
485+
case ERROR_ACCESS_DENIED:
486+
case ERROR_SHARING_VIOLATION:
487+
case ERROR_LOCK_VIOLATION:
488+
case ERROR_SHARING_BUFFER_EXCEEDED:
489+
errno = EACCES;
490+
break;
491+
case ERROR_BUFFER_OVERFLOW:
492+
errno = ENAMETOOLONG;
493+
break;
494+
case ERROR_NOT_ENOUGH_MEMORY:
495+
errno = ENOMEM;
496+
break;
497+
default:
498+
errno = ENOENT;
499+
break;
500+
}
432501
return -1;
433502
}
434503

@@ -516,16 +585,20 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
516585
{
517586
FILETIME mft, aft;
518587
int fh, rc;
588+
DWORD attrs;
589+
wchar_t wfilename[MAX_PATH];
590+
if (xutftowcs_path(wfilename, file_name) < 0)
591+
return -1;
519592

520593
/* must have write permission */
521-
DWORD attrs = GetFileAttributes(file_name);
594+
attrs = GetFileAttributesW(wfilename);
522595
if (attrs != INVALID_FILE_ATTRIBUTES &&
523596
(attrs & FILE_ATTRIBUTE_READONLY)) {
524597
/* ignore errors here; open() will report them */
525-
SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY);
598+
SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY);
526599
}
527600

528-
if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) {
601+
if ((fh = _wopen(wfilename, O_RDWR | O_BINARY)) < 0) {
529602
rc = -1;
530603
goto revert_attrs;
531604
}
@@ -548,7 +621,7 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
548621
if (attrs != INVALID_FILE_ATTRIBUTES &&
549622
(attrs & FILE_ATTRIBUTE_READONLY)) {
550623
/* ignore errors again */
551-
SetFileAttributes(file_name, attrs);
624+
SetFileAttributesW(wfilename, attrs);
552625
}
553626
return rc;
554627
}
@@ -559,6 +632,18 @@ unsigned int sleep (unsigned int seconds)
559632
return 0;
560633
}
561634

635+
char *mingw_mktemp(char *template)
636+
{
637+
wchar_t wtemplate[MAX_PATH];
638+
if (xutftowcs_path(wtemplate, template) < 0)
639+
return NULL;
640+
if (!_wmktemp(wtemplate))
641+
return NULL;
642+
if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
643+
return NULL;
644+
return template;
645+
}
646+
562647
int mkstemp(char *template)
563648
{
564649
char *filename = mktemp(template);
@@ -617,17 +702,18 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
617702
return result;
618703
}
619704

620-
#undef getcwd
621705
char *mingw_getcwd(char *pointer, int len)
622706
{
623707
int i;
624-
char *ret = getcwd(pointer, len);
625-
if (!ret)
626-
return ret;
708+
wchar_t wpointer[MAX_PATH];
709+
if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer)))
710+
return NULL;
711+
if (xwcstoutf(pointer, wpointer, len) < 0)
712+
return NULL;
627713
for (i = 0; pointer[i]; i++)
628714
if (pointer[i] == '\\')
629715
pointer[i] = '/';
630-
return ret;
716+
return pointer;
631717
}
632718

633719
/*
@@ -1485,33 +1571,36 @@ int mingw_rename(const char *pold, const char *pnew)
14851571
{
14861572
DWORD attrs, gle;
14871573
int tries = 0;
1574+
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
1575+
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
1576+
return -1;
14881577

14891578
/*
14901579
* Try native rename() first to get errno right.
14911580
* It is based on MoveFile(), which cannot overwrite existing files.
14921581
*/
1493-
if (!rename(pold, pnew))
1582+
if (!_wrename(wpold, wpnew))
14941583
return 0;
14951584
if (errno != EEXIST)
14961585
return -1;
14971586
repeat:
1498-
if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
1587+
if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
14991588
return 0;
15001589
/* TODO: translate more errors */
15011590
gle = GetLastError();
15021591
if (gle == ERROR_ACCESS_DENIED &&
1503-
(attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
1592+
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
15041593
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
15051594
errno = EISDIR;
15061595
return -1;
15071596
}
15081597
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
1509-
SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
1510-
if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
1598+
SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
1599+
if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
15111600
return 0;
15121601
gle = GetLastError();
15131602
/* revert file attributes on failure */
1514-
SetFileAttributes(pnew, attrs);
1603+
SetFileAttributesW(wpnew, attrs);
15151604
}
15161605
}
15171606
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
@@ -1757,19 +1846,24 @@ void mingw_open_html(const char *unixpath)
17571846

17581847
int link(const char *oldpath, const char *newpath)
17591848
{
1760-
typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
1849+
typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
17611850
static T create_hard_link = NULL;
1851+
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
1852+
if (xutftowcs_path(woldpath, oldpath) < 0 ||
1853+
xutftowcs_path(wnewpath, newpath) < 0)
1854+
return -1;
1855+
17621856
if (!create_hard_link) {
17631857
create_hard_link = (T) GetProcAddress(
1764-
GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
1858+
GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
17651859
if (!create_hard_link)
17661860
create_hard_link = (T)-1;
17671861
}
17681862
if (create_hard_link == (T)-1) {
17691863
errno = ENOSYS;
17701864
return -1;
17711865
}
1772-
if (!create_hard_link(newpath, oldpath, NULL)) {
1866+
if (!create_hard_link(wnewpath, woldpath, NULL)) {
17731867
errno = err_win_to_posix(GetLastError());
17741868
return -1;
17751869
}

0 commit comments

Comments
 (0)