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 "../futils.h"
15 #include "repository.h"
24 #ifndef FILE_NAME_NORMALIZED
25 # define FILE_NAME_NORMALIZED 0
28 #ifndef IO_REPARSE_TAG_SYMLINK
29 #define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
32 #ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
33 # define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x02
36 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
37 # define SYMBOLIC_LINK_FLAG_DIRECTORY 0x01
40 /* Allowable mode bits on Win32. Using mode bits that are not supported on
41 * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
42 * so we simply remove them.
44 #define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
46 unsigned long git_win32__createfile_sharemode
=
47 FILE_SHARE_READ
| FILE_SHARE_WRITE
;
48 int git_win32__retries
= 10;
50 GIT_INLINE(void) set_errno(void)
52 switch (GetLastError()) {
53 case ERROR_FILE_NOT_FOUND
:
54 case ERROR_PATH_NOT_FOUND
:
55 case ERROR_INVALID_DRIVE
:
56 case ERROR_NO_MORE_FILES
:
57 case ERROR_BAD_NETPATH
:
58 case ERROR_BAD_NET_NAME
:
59 case ERROR_BAD_PATHNAME
:
60 case ERROR_FILENAME_EXCED_RANGE
:
63 case ERROR_BAD_ENVIRONMENT
:
66 case ERROR_BAD_FORMAT
:
67 case ERROR_INVALID_STARTING_CODESEG
:
68 case ERROR_INVALID_STACKSEG
:
69 case ERROR_INVALID_MODULETYPE
:
70 case ERROR_INVALID_EXE_SIGNATURE
:
71 case ERROR_EXE_MARKED_INVALID
:
72 case ERROR_BAD_EXE_FORMAT
:
73 case ERROR_ITERATED_DATA_EXCEEDS_64k
:
74 case ERROR_INVALID_MINALLOCSIZE
:
75 case ERROR_DYNLINK_FROM_INVALID_RING
:
76 case ERROR_IOPL_NOT_ENABLED
:
77 case ERROR_INVALID_SEGDPL
:
78 case ERROR_AUTODATASEG_EXCEEDS_64k
:
79 case ERROR_RING2SEG_MUST_BE_MOVABLE
:
80 case ERROR_RELOC_CHAIN_XEEDS_SEGLIM
:
81 case ERROR_INFLOOP_IN_RELOC_CHAIN
:
84 case ERROR_INVALID_HANDLE
:
85 case ERROR_INVALID_TARGET_HANDLE
:
86 case ERROR_DIRECT_ACCESS_HANDLE
:
89 case ERROR_WAIT_NO_CHILDREN
:
90 case ERROR_CHILD_NOT_COMPLETE
:
93 case ERROR_NO_PROC_SLOTS
:
94 case ERROR_MAX_THRDS_REACHED
:
95 case ERROR_NESTING_NOT_ALLOWED
:
98 case ERROR_ARENA_TRASHED
:
99 case ERROR_NOT_ENOUGH_MEMORY
:
100 case ERROR_INVALID_BLOCK
:
101 case ERROR_NOT_ENOUGH_QUOTA
:
104 case ERROR_ACCESS_DENIED
:
105 case ERROR_CURRENT_DIRECTORY
:
106 case ERROR_WRITE_PROTECT
:
108 case ERROR_NOT_READY
:
109 case ERROR_BAD_COMMAND
:
111 case ERROR_BAD_LENGTH
:
113 case ERROR_NOT_DOS_DISK
:
114 case ERROR_SECTOR_NOT_FOUND
:
115 case ERROR_OUT_OF_PAPER
:
116 case ERROR_WRITE_FAULT
:
117 case ERROR_READ_FAULT
:
118 case ERROR_GEN_FAILURE
:
119 case ERROR_SHARING_VIOLATION
:
120 case ERROR_LOCK_VIOLATION
:
121 case ERROR_WRONG_DISK
:
122 case ERROR_SHARING_BUFFER_EXCEEDED
:
123 case ERROR_NETWORK_ACCESS_DENIED
:
124 case ERROR_CANNOT_MAKE
:
126 case ERROR_DRIVE_LOCKED
:
127 case ERROR_SEEK_ON_DEVICE
:
128 case ERROR_NOT_LOCKED
:
129 case ERROR_LOCK_FAILED
:
132 case ERROR_FILE_EXISTS
:
133 case ERROR_ALREADY_EXISTS
:
136 case ERROR_NOT_SAME_DEVICE
:
139 case ERROR_INVALID_FUNCTION
:
140 case ERROR_INVALID_ACCESS
:
141 case ERROR_INVALID_DATA
:
142 case ERROR_INVALID_PARAMETER
:
143 case ERROR_NEGATIVE_SEEK
:
146 case ERROR_TOO_MANY_OPEN_FILES
:
149 case ERROR_DISK_FULL
:
152 case ERROR_BROKEN_PIPE
:
155 case ERROR_DIR_NOT_EMPTY
:
163 GIT_INLINE(bool) last_error_retryable(void)
165 int os_error
= GetLastError();
167 return (os_error
== ERROR_SHARING_VIOLATION
||
168 os_error
== ERROR_ACCESS_DENIED
);
171 #define do_with_retries(fn, remediation) \
173 int __retry, __ret; \
174 for (__retry = git_win32__retries; __retry; __retry--) { \
175 if ((__ret = (fn)) != GIT_RETRY) \
177 if (__retry > 1 && (__ret = (remediation)) != 0) { \
178 if (__ret == GIT_RETRY) \
187 static int ensure_writable(wchar_t *path)
191 if ((attrs
= GetFileAttributesW(path
)) == INVALID_FILE_ATTRIBUTES
)
194 if ((attrs
& FILE_ATTRIBUTE_READONLY
) == 0)
197 if (!SetFileAttributesW(path
, (attrs
& ~FILE_ATTRIBUTE_READONLY
)))
208 * Truncate or extend file.
210 * We now take a "git_off_t" rather than "long" because
211 * files may be longer than 2Gb.
213 int p_ftruncate(int fd
, off64_t size
)
220 #if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
221 return ((_chsize_s(fd
, size
) == 0) ? 0 : -1);
223 /* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
224 if (size
> INT32_MAX
) {
228 return _chsize(fd
, (long)size
);
232 int p_mkdir(const char *path
, mode_t mode
)
238 if (git_win32_path_from_utf8(buf
, path
) < 0)
244 int p_link(const char *old
, const char *new)
252 GIT_INLINE(int) unlink_once(const wchar_t *path
)
256 if (DeleteFileW(path
))
259 if ((error
= GetLastError()) == ERROR_ACCESS_DENIED
) {
260 WIN32_FILE_ATTRIBUTE_DATA fdata
;
261 if (!GetFileAttributesExW(path
, GetFileExInfoStandard
, &fdata
) ||
262 !(fdata
.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT
) ||
263 !(fdata
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
266 if (RemoveDirectoryW(path
))
273 if (last_error_retryable())
280 int p_unlink(const char *path
)
282 git_win32_path wpath
;
284 if (git_win32_path_from_utf8(wpath
, path
) < 0)
287 do_with_retries(unlink_once(wpath
), ensure_writable(wpath
));
292 HANDLE fh
= (HANDLE
)_get_osfhandle(fd
);
296 if (fh
== INVALID_HANDLE_VALUE
) {
301 if (!FlushFileBuffers(fh
)) {
302 DWORD code
= GetLastError();
304 if (code
== ERROR_INVALID_HANDLE
)
315 #define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
322 WIN32_FILE_ATTRIBUTE_DATA fdata
;
324 if (GetFileAttributesExW(path
, GetFileExInfoStandard
, &fdata
)) {
328 return git_win32__file_attribute_to_stat(buf
, &fdata
, path
);
331 switch (GetLastError()) {
332 case ERROR_ACCESS_DENIED
:
340 /* To match POSIX behavior, set ENOTDIR when any of the folders in the
341 * file path is a regular file, otherwise set ENOENT.
343 if (errno
== ENOENT
&& posix_enotdir
) {
344 size_t path_len
= wcslen(path
);
346 /* scan up path until we find an existing item */
350 /* remove last directory component */
351 for (path_len
--; path_len
> 0 && !WIN32_IS_WSEP(path
[path_len
]); path_len
--);
356 path
[path_len
] = L
'\0';
357 attrs
= GetFileAttributesW(path
);
359 if (attrs
!= INVALID_FILE_ATTRIBUTES
) {
360 if (!(attrs
& FILE_ATTRIBUTE_DIRECTORY
))
370 static int do_lstat(const char *path
, struct stat
*buf
, bool posixly_correct
)
372 git_win32_path path_w
;
375 if ((len
= git_win32_path_from_utf8(path_w
, path
)) < 0)
378 git_win32_path_trim_end(path_w
, len
);
380 return lstat_w(path_w
, buf
, posixly_correct
);
383 int p_lstat(const char *filename
, struct stat
*buf
)
385 return do_lstat(filename
, buf
, false);
388 int p_lstat_posixly(const char *filename
, struct stat
*buf
)
390 return do_lstat(filename
, buf
, true);
393 int p_readlink(const char *path
, char *buf
, size_t bufsiz
)
395 git_win32_path path_w
, target_w
;
396 git_win32_utf8_path target
;
399 /* readlink(2) does not NULL-terminate the string written
400 * to the target buffer. Furthermore, the target buffer need
401 * not be large enough to hold the entire result. A truncated
402 * result should be written in this case. Since this truncation
403 * could occur in the middle of the encoding of a code point,
404 * we need to buffer the result on the stack. */
406 if (git_win32_path_from_utf8(path_w
, path
) < 0 ||
407 git_win32_path_readlink_w(target_w
, path_w
) < 0 ||
408 (len
= git_win32_path_to_utf8(target
, target_w
)) < 0)
411 bufsiz
= min((size_t)len
, bufsiz
);
412 memcpy(buf
, target
, bufsiz
);
417 static bool target_is_dir(const char *target
, const char *path
)
419 git_buf resolved
= GIT_BUF_INIT
;
420 git_win32_path resolved_w
;
423 if (git_path_is_absolute(target
))
424 git_win32_path_from_utf8(resolved_w
, target
);
425 else if (git_path_dirname_r(&resolved
, path
) < 0 ||
426 git_path_apply_relative(&resolved
, target
) < 0 ||
427 git_win32_path_from_utf8(resolved_w
, resolved
.ptr
) < 0)
430 isdir
= GetFileAttributesW(resolved_w
) & FILE_ATTRIBUTE_DIRECTORY
;
433 git_buf_dispose(&resolved
);
437 int p_symlink(const char *target
, const char *path
)
439 git_win32_path target_w
, path_w
;
443 * Convert both target and path to Windows-style paths. Note that we do
444 * not want to use `git_win32_path_from_utf8` for converting the target,
445 * as that function will automatically pre-pend the current working
446 * directory in case the path is not absolute. As Git will instead use
447 * relative symlinks, this is not someting we want.
449 if (git_win32_path_from_utf8(path_w
, path
) < 0 ||
450 git__utf8_to_16(target_w
, MAX_PATH
, target
) < 0 ||
451 git_win32_path_canonicalize(target_w
) < 0)
454 dwFlags
= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
;
455 if (target_is_dir(target
, path
))
456 dwFlags
|= SYMBOLIC_LINK_FLAG_DIRECTORY
;
458 if (!CreateSymbolicLinkW(path_w
, target_w
, dwFlags
))
467 SECURITY_ATTRIBUTES security
;
468 DWORD creation_disposition
;
473 GIT_INLINE(void) open_opts_from_posix(struct open_opts
*opts
, int flags
, mode_t mode
)
475 memset(opts
, 0, sizeof(struct open_opts
));
477 switch (flags
& (O_WRONLY
| O_RDWR
)) {
479 opts
->access
= GENERIC_WRITE
;
482 opts
->access
= GENERIC_READ
| GENERIC_WRITE
;
485 opts
->access
= GENERIC_READ
;
489 opts
->sharing
= (DWORD
)git_win32__createfile_sharemode
;
491 switch (flags
& (O_CREAT
| O_TRUNC
| O_EXCL
)) {
492 case O_CREAT
| O_EXCL
:
493 case O_CREAT
| O_TRUNC
| O_EXCL
:
494 opts
->creation_disposition
= CREATE_NEW
;
496 case O_CREAT
| O_TRUNC
:
497 opts
->creation_disposition
= CREATE_ALWAYS
;
500 opts
->creation_disposition
= TRUNCATE_EXISTING
;
503 opts
->creation_disposition
= OPEN_ALWAYS
;
506 opts
->creation_disposition
= OPEN_EXISTING
;
510 opts
->attributes
= ((flags
& O_CREAT
) && !(mode
& S_IWRITE
)) ?
511 FILE_ATTRIBUTE_READONLY
: FILE_ATTRIBUTE_NORMAL
;
512 opts
->osf_flags
= flags
& (O_RDONLY
| O_APPEND
);
514 opts
->security
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
515 opts
->security
.lpSecurityDescriptor
= NULL
;
516 opts
->security
.bInheritHandle
= 0;
519 GIT_INLINE(int) open_once(
521 struct open_opts
*opts
)
525 HANDLE handle
= CreateFileW(path
, opts
->access
, opts
->sharing
,
526 &opts
->security
, opts
->creation_disposition
, opts
->attributes
, 0);
528 if (handle
== INVALID_HANDLE_VALUE
) {
529 if (last_error_retryable())
536 if ((fd
= _open_osfhandle((intptr_t)handle
, opts
->osf_flags
)) < 0)
542 int p_open(const char *path
, int flags
, ...)
544 git_win32_path wpath
;
546 struct open_opts opts
= {0};
548 if (git_win32_path_from_utf8(wpath
, path
) < 0)
551 if (flags
& O_CREAT
) {
554 va_start(arg_list
, flags
);
555 mode
= (mode_t
)va_arg(arg_list
, int);
559 open_opts_from_posix(&opts
, flags
, mode
);
562 open_once(wpath
, &opts
),
566 int p_creat(const char *path
, mode_t mode
)
568 return p_open(path
, O_WRONLY
| O_CREAT
| O_TRUNC
, mode
);
571 int p_utimes(const char *path
, const struct p_timeval times
[2])
573 git_win32_path wpath
;
575 DWORD attrs_orig
, attrs_new
= 0;
576 struct open_opts opts
= { 0 };
578 if (git_win32_path_from_utf8(wpath
, path
) < 0)
581 attrs_orig
= GetFileAttributesW(wpath
);
583 if (attrs_orig
& FILE_ATTRIBUTE_READONLY
) {
584 attrs_new
= attrs_orig
& ~FILE_ATTRIBUTE_READONLY
;
586 if (!SetFileAttributesW(wpath
, attrs_new
)) {
587 git_error_set(GIT_ERROR_OS
, "failed to set attributes");
592 open_opts_from_posix(&opts
, O_RDWR
, 0);
594 if ((fd
= open_once(wpath
, &opts
)) < 0) {
599 error
= p_futimes(fd
, times
);
603 if (attrs_orig
!= attrs_new
) {
604 DWORD os_error
= GetLastError();
605 SetFileAttributesW(wpath
, attrs_orig
);
606 SetLastError(os_error
);
612 int p_futimes(int fd
, const struct p_timeval times
[2])
615 FILETIME atime
= { 0 }, mtime
= { 0 };
621 SystemTimeToFileTime(&st
, &atime
);
622 SystemTimeToFileTime(&st
, &mtime
);
625 git_win32__timeval_to_filetime(&atime
, times
[0]);
626 git_win32__timeval_to_filetime(&mtime
, times
[1]);
629 if ((handle
= (HANDLE
)_get_osfhandle(fd
)) == INVALID_HANDLE_VALUE
)
632 if (SetFileTime(handle
, NULL
, &atime
, &mtime
) == 0)
638 int p_getcwd(char *buffer_out
, size_t size
)
641 wchar_t *cwd
= _wgetcwd(buf
, GIT_WIN_PATH_UTF16
);
646 /* Convert the working directory back to UTF-8 */
647 if (git__utf16_to_8(buffer_out
, size
, cwd
) < 0) {
648 DWORD code
= GetLastError();
650 if (code
== ERROR_INSUFFICIENT_BUFFER
)
661 static int getfinalpath_w(
668 /* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
669 * specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
670 * target of the link. */
671 hFile
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_DELETE
,
672 NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
674 if (INVALID_HANDLE_VALUE
== hFile
)
677 /* Call GetFinalPathNameByHandle */
678 dwChars
= GetFinalPathNameByHandleW(hFile
, dest
, GIT_WIN_PATH_UTF16
, FILE_NAME_NORMALIZED
);
681 if (!dwChars
|| dwChars
>= GIT_WIN_PATH_UTF16
)
684 /* The path may be delivered to us with a namespace prefix; remove */
685 return (int)git_win32_path_remove_namespace(dest
, dwChars
);
688 static int follow_and_lstat_link(git_win32_path path
, struct stat
* buf
)
690 git_win32_path target_w
;
692 if (getfinalpath_w(target_w
, path
) < 0)
695 return lstat_w(target_w
, buf
, false);
698 int p_fstat(int fd
, struct stat
*buf
)
700 BY_HANDLE_FILE_INFORMATION fhInfo
;
702 HANDLE fh
= (HANDLE
)_get_osfhandle(fd
);
704 if (fh
== INVALID_HANDLE_VALUE
||
705 !GetFileInformationByHandle(fh
, &fhInfo
)) {
710 git_win32__file_information_to_stat(buf
, &fhInfo
);
714 int p_stat(const char* path
, struct stat
* buf
)
716 git_win32_path path_w
;
719 if ((len
= git_win32_path_from_utf8(path_w
, path
)) < 0 ||
720 lstat_w(path_w
, buf
, false) < 0)
723 /* The item is a symbolic link or mount point. No need to iterate
724 * to follow multiple links; use GetFinalPathNameFromHandle. */
725 if (S_ISLNK(buf
->st_mode
))
726 return follow_and_lstat_link(path_w
, buf
);
731 int p_chdir(const char* path
)
735 if (git_win32_path_from_utf8(buf
, path
) < 0)
741 int p_chmod(const char* path
, mode_t mode
)
745 if (git_win32_path_from_utf8(buf
, path
) < 0)
748 return _wchmod(buf
, mode
);
751 int p_rmdir(const char* path
)
756 if (git_win32_path_from_utf8(buf
, path
) < 0)
759 error
= _wrmdir(buf
);
762 switch (GetLastError()) {
763 /* _wrmdir() is documented to return EACCES if "A program has an open
764 * handle to the directory." This sounds like what everybody else calls
765 * EBUSY. Let's convert appropriate error codes.
767 case ERROR_SHARING_VIOLATION
:
771 /* This error can be returned when trying to rmdir an extant file. */
772 case ERROR_DIRECTORY
:
781 char *p_realpath(const char *orig_path
, char *buffer
)
783 git_win32_path orig_path_w
, buffer_w
;
785 if (git_win32_path_from_utf8(orig_path_w
, orig_path
) < 0)
788 /* Note that if the path provided is a relative path, then the current directory
789 * is used to resolve the path -- which is a concurrency issue because the current
790 * directory is a process-wide variable. */
791 if (!GetFullPathNameW(orig_path_w
, GIT_WIN_PATH_UTF16
, buffer_w
, NULL
)) {
792 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
793 errno
= ENAMETOOLONG
;
800 /* The path must exist. */
801 if (GetFileAttributesW(buffer_w
) == INVALID_FILE_ATTRIBUTES
) {
806 if (!buffer
&& !(buffer
= git__malloc(GIT_WIN_PATH_UTF8
))) {
811 /* Convert the path to UTF-8. If the caller provided a buffer, then it
812 * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
813 * then we may overflow. */
814 if (git_win32_path_to_utf8(buffer
, buffer_w
) < 0)
817 git_path_mkposix(buffer
);
822 int p_vsnprintf(char *buffer
, size_t count
, const char *format
, va_list argptr
)
824 #if defined(_MSC_VER)
828 return _vscprintf(format
, argptr
);
831 len
= _vsnprintf_s(buffer
, count
, _TRUNCATE
, format
, argptr
);
833 len
= _vsnprintf(buffer
, count
, format
, argptr
);
837 return _vscprintf(format
, argptr
);
841 return vsnprintf(buffer
, count
, format
, argptr
);
845 int p_snprintf(char *buffer
, size_t count
, const char *format
, ...)
850 va_start(va
, format
);
851 r
= p_vsnprintf(buffer
, count
, format
, va
);
858 int p_mkstemp(char *tmp_path
)
860 #if defined(_MSC_VER) && _MSC_VER >= 1500
861 if (_mktemp_s(tmp_path
, strlen(tmp_path
) + 1) != 0)
864 if (_mktemp(tmp_path
) == NULL
)
868 return p_open(tmp_path
, O_RDWR
| O_CREAT
| O_EXCL
, 0744); /* -V536 */
871 int p_access(const char* path
, mode_t mode
)
875 if (git_win32_path_from_utf8(buf
, path
) < 0)
878 return _waccess(buf
, mode
& WIN32_MODE_MASK
);
881 GIT_INLINE(int) rename_once(const wchar_t *from
, const wchar_t *to
)
883 if (MoveFileExW(from
, to
, MOVEFILE_REPLACE_EXISTING
| MOVEFILE_COPY_ALLOWED
))
886 if (last_error_retryable())
893 int p_rename(const char *from
, const char *to
)
895 git_win32_path wfrom
, wto
;
897 if (git_win32_path_from_utf8(wfrom
, from
) < 0 ||
898 git_win32_path_from_utf8(wto
, to
) < 0)
901 do_with_retries(rename_once(wfrom
, wto
), ensure_writable(wto
));
904 int p_recv(GIT_SOCKET socket
, void *buffer
, size_t length
, int flags
)
906 if ((size_t)((int)length
) != length
)
907 return -1; /* git_error_set will be done by caller */
909 return recv(socket
, buffer
, (int)length
, flags
);
912 int p_send(GIT_SOCKET socket
, const void *buffer
, size_t length
, int flags
)
914 if ((size_t)((int)length
) != length
)
915 return -1; /* git_error_set will be done by caller */
917 return send(socket
, buffer
, (int)length
, flags
);
921 * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
922 * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
925 p_localtime_r (const time_t *timer
, struct tm
*result
)
927 struct tm
*local_result
;
928 local_result
= localtime (timer
);
930 if (local_result
== NULL
|| result
== NULL
)
933 memcpy (result
, local_result
, sizeof (struct tm
));
937 p_gmtime_r (const time_t *timer
, struct tm
*result
)
939 struct tm
*local_result
;
940 local_result
= gmtime (timer
);
942 if (local_result
== NULL
|| result
== NULL
)
945 memcpy (result
, local_result
, sizeof (struct tm
));
949 int p_inet_pton(int af
, const char *src
, void *dst
)
951 struct sockaddr_storage sin
;
953 int sin_len
= sizeof(struct sockaddr_storage
), addr_len
;
957 addr
= &((struct sockaddr_in
*)&sin
)->sin_addr
;
958 addr_len
= sizeof(struct in_addr
);
959 } else if (af
== AF_INET6
) {
960 addr
= &((struct sockaddr_in6
*)&sin
)->sin6_addr
;
961 addr_len
= sizeof(struct in6_addr
);
963 errno
= EAFNOSUPPORT
;
967 if ((error
= WSAStringToAddressA((LPSTR
)src
, af
, NULL
, (LPSOCKADDR
)&sin
, &sin_len
)) == 0) {
968 memcpy(dst
, addr
, addr_len
);
972 switch(WSAGetLastError()) {
978 case WSA_NOT_ENOUGH_MEMORY
: