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.
8 #include "../fileops.h"
12 #include "repository.h"
21 #ifndef FILE_NAME_NORMALIZED
22 # define FILE_NAME_NORMALIZED 0
25 #ifndef IO_REPARSE_TAG_SYMLINK
26 #define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
29 /* Options which we always provide to _wopen.
31 * _O_BINARY - Raw access; no translation of CR or LF characters
32 * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
33 * The Windows default is 'not inheritable', but the CRT's default (following
34 * POSIX convention) is 'inheritable'. We have no desire for our handles to be
35 * inheritable on Windows, so specify the flag to get default behavior back. */
36 #define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)
38 /* Allowable mode bits on Win32. Using mode bits that are not supported on
39 * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
40 * so we simply remove them.
42 #define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
44 /* GetFinalPathNameByHandleW signature */
45 typedef DWORD(WINAPI
*PFGetFinalPathNameByHandleW
)(HANDLE
, LPWSTR
, DWORD
, DWORD
);
48 * Truncate or extend file.
50 * We now take a "git_off_t" rather than "long" because
51 * files may be longer than 2Gb.
53 int p_ftruncate(int fd
, git_off_t size
)
60 #if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
61 return ((_chsize_s(fd
, size
) == 0) ? 0 : -1);
63 /* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
64 if (size
> INT32_MAX
) {
68 return _chsize(fd
, (long)size
);
72 int p_mkdir(const char *path
, mode_t mode
)
78 if (git_win32_path_from_utf8(buf
, path
) < 0)
84 int p_link(const char *old
, const char *new)
92 int p_unlink(const char *path
)
97 if (git_win32_path_from_utf8(buf
, path
) < 0)
100 error
= _wunlink(buf
);
102 /* If the file could not be deleted because it was
103 * read-only, clear the bit and try again */
104 if (error
== -1 && errno
== EACCES
) {
106 error
= _wunlink(buf
);
114 HANDLE fh
= (HANDLE
)_get_osfhandle(fd
);
118 if (fh
== INVALID_HANDLE_VALUE
) {
123 if (!FlushFileBuffers(fh
)) {
124 DWORD code
= GetLastError();
126 if (code
== ERROR_INVALID_HANDLE
)
137 #define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
144 WIN32_FILE_ATTRIBUTE_DATA fdata
;
146 if (GetFileAttributesExW(path
, GetFileExInfoStandard
, &fdata
)) {
150 return git_win32__file_attribute_to_stat(buf
, &fdata
, path
);
153 switch (GetLastError()) {
154 case ERROR_ACCESS_DENIED
:
162 /* To match POSIX behavior, set ENOTDIR when any of the folders in the
163 * file path is a regular file, otherwise set ENOENT.
165 if (errno
== ENOENT
&& posix_enotdir
) {
166 size_t path_len
= wcslen(path
);
168 /* scan up path until we find an existing item */
172 /* remove last directory component */
173 for (path_len
--; path_len
> 0 && !WIN32_IS_WSEP(path
[path_len
]); path_len
--);
178 path
[path_len
] = L
'\0';
179 attrs
= GetFileAttributesW(path
);
181 if (attrs
!= INVALID_FILE_ATTRIBUTES
) {
182 if (!(attrs
& FILE_ATTRIBUTE_DIRECTORY
))
192 static int do_lstat(const char *path
, struct stat
*buf
, bool posixly_correct
)
194 git_win32_path path_w
;
197 if ((len
= git_win32_path_from_utf8(path_w
, path
)) < 0)
200 git_win32__path_trim_end(path_w
, len
);
202 return lstat_w(path_w
, buf
, posixly_correct
);
205 int p_lstat(const char *filename
, struct stat
*buf
)
207 return do_lstat(filename
, buf
, false);
210 int p_lstat_posixly(const char *filename
, struct stat
*buf
)
212 return do_lstat(filename
, buf
, true);
215 int p_utimes(const char *filename
, const struct p_timeval times
[2])
219 if ((fd
= p_open(filename
, O_RDWR
)) < 0)
222 error
= p_futimes(fd
, times
);
228 int p_futimes(int fd
, const struct p_timeval times
[2])
231 FILETIME atime
= {0}, mtime
= {0};
237 SystemTimeToFileTime(&st
, &atime
);
238 SystemTimeToFileTime(&st
, &mtime
);
240 git_win32__timeval_to_filetime(&atime
, times
[0]);
241 git_win32__timeval_to_filetime(&mtime
, times
[1]);
244 if ((handle
= (HANDLE
)_get_osfhandle(fd
)) == INVALID_HANDLE_VALUE
)
247 if (SetFileTime(handle
, NULL
, &atime
, &mtime
) == 0)
253 int p_readlink(const char *path
, char *buf
, size_t bufsiz
)
255 git_win32_path path_w
, target_w
;
256 git_win32_utf8_path target
;
259 /* readlink(2) does not NULL-terminate the string written
260 * to the target buffer. Furthermore, the target buffer need
261 * not be large enough to hold the entire result. A truncated
262 * result should be written in this case. Since this truncation
263 * could occur in the middle of the encoding of a code point,
264 * we need to buffer the result on the stack. */
266 if (git_win32_path_from_utf8(path_w
, path
) < 0 ||
267 git_win32_path_readlink_w(target_w
, path_w
) < 0 ||
268 (len
= git_win32_path_to_utf8(target
, target_w
)) < 0)
271 bufsiz
= min((size_t)len
, bufsiz
);
272 memcpy(buf
, target
, bufsiz
);
277 int p_symlink(const char *old
, const char *new)
279 /* Real symlinks on NTFS require admin privileges. Until this changes,
280 * libgit2 just creates a text file with the link target in the contents.
282 return git_futils_fake_symlink(old
, new);
285 int p_open(const char *path
, int flags
, ...)
290 if (git_win32_path_from_utf8(buf
, path
) < 0)
293 if (flags
& O_CREAT
) {
296 va_start(arg_list
, flags
);
297 mode
= (mode_t
)va_arg(arg_list
, int);
301 return _wopen(buf
, flags
| STANDARD_OPEN_FLAGS
, mode
& WIN32_MODE_MASK
);
304 int p_creat(const char *path
, mode_t mode
)
308 if (git_win32_path_from_utf8(buf
, path
) < 0)
312 _O_WRONLY
| _O_CREAT
| _O_TRUNC
| STANDARD_OPEN_FLAGS
,
313 mode
& WIN32_MODE_MASK
);
316 int p_getcwd(char *buffer_out
, size_t size
)
319 wchar_t *cwd
= _wgetcwd(buf
, GIT_WIN_PATH_UTF16
);
324 /* Convert the working directory back to UTF-8 */
325 if (git__utf16_to_8(buffer_out
, size
, cwd
) < 0) {
326 DWORD code
= GetLastError();
328 if (code
== ERROR_INSUFFICIENT_BUFFER
)
340 * Returns the address of the GetFinalPathNameByHandleW function.
341 * This function is available on Windows Vista and higher.
343 static PFGetFinalPathNameByHandleW
get_fpnbyhandle(void)
345 static PFGetFinalPathNameByHandleW pFunc
= NULL
;
346 PFGetFinalPathNameByHandleW toReturn
= pFunc
;
349 HMODULE hModule
= GetModuleHandleW(L
"kernel32");
352 toReturn
= (PFGetFinalPathNameByHandleW
)GetProcAddress(hModule
, "GetFinalPathNameByHandleW");
362 static int getfinalpath_w(
366 PFGetFinalPathNameByHandleW pgfp
= get_fpnbyhandle();
373 /* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
374 * specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
375 * target of the link. */
376 hFile
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_DELETE
,
377 NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
379 if (INVALID_HANDLE_VALUE
== hFile
)
382 /* Call GetFinalPathNameByHandle */
383 dwChars
= pgfp(hFile
, dest
, GIT_WIN_PATH_UTF16
, FILE_NAME_NORMALIZED
);
386 if (!dwChars
|| dwChars
>= GIT_WIN_PATH_UTF16
)
389 /* The path may be delivered to us with a prefix; canonicalize */
390 return (int)git_win32__canonicalize_path(dest
, dwChars
);
393 static int follow_and_lstat_link(git_win32_path path
, struct stat
* buf
)
395 git_win32_path target_w
;
397 if (getfinalpath_w(target_w
, path
) < 0)
400 return lstat_w(target_w
, buf
, false);
403 int p_fstat(int fd
, struct stat
*buf
)
405 BY_HANDLE_FILE_INFORMATION fhInfo
;
407 HANDLE fh
= (HANDLE
)_get_osfhandle(fd
);
409 if (fh
== INVALID_HANDLE_VALUE
||
410 !GetFileInformationByHandle(fh
, &fhInfo
)) {
415 git_win32__file_information_to_stat(buf
, &fhInfo
);
419 int p_stat(const char* path
, struct stat
* buf
)
421 git_win32_path path_w
;
424 if ((len
= git_win32_path_from_utf8(path_w
, path
)) < 0 ||
425 lstat_w(path_w
, buf
, false) < 0)
428 /* The item is a symbolic link or mount point. No need to iterate
429 * to follow multiple links; use GetFinalPathNameFromHandle. */
430 if (S_ISLNK(buf
->st_mode
))
431 return follow_and_lstat_link(path_w
, buf
);
436 int p_chdir(const char* path
)
440 if (git_win32_path_from_utf8(buf
, path
) < 0)
446 int p_chmod(const char* path
, mode_t mode
)
450 if (git_win32_path_from_utf8(buf
, path
) < 0)
453 return _wchmod(buf
, mode
);
456 int p_rmdir(const char* path
)
461 if (git_win32_path_from_utf8(buf
, path
) < 0)
464 error
= _wrmdir(buf
);
467 switch (GetLastError()) {
468 /* _wrmdir() is documented to return EACCES if "A program has an open
469 * handle to the directory." This sounds like what everybody else calls
470 * EBUSY. Let's convert appropriate error codes.
472 case ERROR_SHARING_VIOLATION
:
476 /* This error can be returned when trying to rmdir an extant file. */
477 case ERROR_DIRECTORY
:
486 char *p_realpath(const char *orig_path
, char *buffer
)
488 git_win32_path orig_path_w
, buffer_w
;
490 if (git_win32_path_from_utf8(orig_path_w
, orig_path
) < 0)
493 /* Note that if the path provided is a relative path, then the current directory
494 * is used to resolve the path -- which is a concurrency issue because the current
495 * directory is a process-wide variable. */
496 if (!GetFullPathNameW(orig_path_w
, GIT_WIN_PATH_UTF16
, buffer_w
, NULL
)) {
497 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
498 errno
= ENAMETOOLONG
;
505 /* The path must exist. */
506 if (GetFileAttributesW(buffer_w
) == INVALID_FILE_ATTRIBUTES
) {
511 if (!buffer
&& !(buffer
= git__malloc(GIT_WIN_PATH_UTF8
))) {
516 /* Convert the path to UTF-8. If the caller provided a buffer, then it
517 * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
518 * then we may overflow. */
519 if (git_win32_path_to_utf8(buffer
, buffer_w
) < 0)
522 git_path_mkposix(buffer
);
527 int p_vsnprintf(char *buffer
, size_t count
, const char *format
, va_list argptr
)
529 #if defined(_MSC_VER)
533 return _vscprintf(format
, argptr
);
536 len
= _vsnprintf_s(buffer
, count
, _TRUNCATE
, format
, argptr
);
538 len
= _vsnprintf(buffer
, count
, format
, argptr
);
542 return _vscprintf(format
, argptr
);
546 return vsnprintf(buffer
, count
, format
, argptr
);
550 int p_snprintf(char *buffer
, size_t count
, const char *format
, ...)
555 va_start(va
, format
);
556 r
= p_vsnprintf(buffer
, count
, format
, va
);
563 int p_mkstemp(char *tmp_path
)
565 #if defined(_MSC_VER) && _MSC_VER >= 1500
566 if (_mktemp_s(tmp_path
, strlen(tmp_path
) + 1) != 0)
569 if (_mktemp(tmp_path
) == NULL
)
573 return p_open(tmp_path
, O_RDWR
| O_CREAT
| O_EXCL
, 0744); //-V536
576 int p_access(const char* path
, mode_t mode
)
580 if (git_win32_path_from_utf8(buf
, path
) < 0)
583 return _waccess(buf
, mode
& WIN32_MODE_MASK
);
586 static int ensure_writable(wchar_t *fpath
)
590 attrs
= GetFileAttributesW(fpath
);
591 if (attrs
== INVALID_FILE_ATTRIBUTES
) {
592 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
595 giterr_set(GITERR_OS
, "failed to get attributes");
599 if (!(attrs
& FILE_ATTRIBUTE_READONLY
))
602 attrs
&= ~FILE_ATTRIBUTE_READONLY
;
603 if (!SetFileAttributesW(fpath
, attrs
)) {
604 giterr_set(GITERR_OS
, "failed to set attributes");
611 int p_rename(const char *from
, const char *to
)
613 git_win32_path wfrom
;
616 int rename_succeeded
;
619 if (git_win32_path_from_utf8(wfrom
, from
) < 0 ||
620 git_win32_path_from_utf8(wto
, to
) < 0)
623 /* wait up to 50ms if file is locked by another thread or process */
625 rename_succeeded
= 0;
626 while (rename_tries
< 10) {
627 if (ensure_writable(wto
) == 0 &&
628 MoveFileExW(wfrom
, wto
, MOVEFILE_REPLACE_EXISTING
| MOVEFILE_COPY_ALLOWED
) != 0) {
629 rename_succeeded
= 1;
633 error
= GetLastError();
634 if (error
== ERROR_SHARING_VIOLATION
|| error
== ERROR_ACCESS_DENIED
) {
641 return rename_succeeded
? 0 : -1;
644 int p_recv(GIT_SOCKET socket
, void *buffer
, size_t length
, int flags
)
646 if ((size_t)((int)length
) != length
)
647 return -1; /* giterr_set will be done by caller */
649 return recv(socket
, buffer
, (int)length
, flags
);
652 int p_send(GIT_SOCKET socket
, const void *buffer
, size_t length
, int flags
)
654 if ((size_t)((int)length
) != length
)
655 return -1; /* giterr_set will be done by caller */
657 return send(socket
, buffer
, (int)length
, flags
);
661 * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
662 * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
665 p_localtime_r (const time_t *timer
, struct tm
*result
)
667 struct tm
*local_result
;
668 local_result
= localtime (timer
);
670 if (local_result
== NULL
|| result
== NULL
)
673 memcpy (result
, local_result
, sizeof (struct tm
));
677 p_gmtime_r (const time_t *timer
, struct tm
*result
)
679 struct tm
*local_result
;
680 local_result
= gmtime (timer
);
682 if (local_result
== NULL
|| result
== NULL
)
685 memcpy (result
, local_result
, sizeof (struct tm
));
689 int p_inet_pton(int af
, const char *src
, void *dst
)
691 struct sockaddr_storage sin
;
693 int sin_len
= sizeof(struct sockaddr_storage
), addr_len
;
697 addr
= &((struct sockaddr_in
*)&sin
)->sin_addr
;
698 addr_len
= sizeof(struct in_addr
);
699 } else if (af
== AF_INET6
) {
700 addr
= &((struct sockaddr_in6
*)&sin
)->sin6_addr
;
701 addr_len
= sizeof(struct in6_addr
);
703 errno
= EAFNOSUPPORT
;
707 if ((error
= WSAStringToAddressA((LPSTR
)src
, af
, NULL
, (LPSOCKADDR
)&sin
, &sin_len
)) == 0) {
708 memcpy(dst
, addr
, addr_len
);
712 switch(WSAGetLastError()) {
718 case WSA_NOT_ENOUGH_MEMORY
: