]> git.proxmox.com Git - libgit2.git/blob - src/win32/posix_w32.c
Use secure API if available.
[libgit2.git] / src / win32 / posix_w32.c
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 #include "../posix.h"
8 #include "../fileops.h"
9 #include "path.h"
10 #include "path_w32.h"
11 #include "utf-conv.h"
12 #include "repository.h"
13 #include "reparse.h"
14 #include <errno.h>
15 #include <io.h>
16 #include <fcntl.h>
17 #include <ws2tcpip.h>
18
19 #ifndef FILE_NAME_NORMALIZED
20 # define FILE_NAME_NORMALIZED 0
21 #endif
22
23 #ifndef IO_REPARSE_TAG_SYMLINK
24 #define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
25 #endif
26
27 /* Options which we always provide to _wopen.
28 *
29 * _O_BINARY - Raw access; no translation of CR or LF characters
30 * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
31 * The Windows default is 'not inheritable', but the CRT's default (following
32 * POSIX convention) is 'inheritable'. We have no desire for our handles to be
33 * inheritable on Windows, so specify the flag to get default behavior back. */
34 #define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)
35
36 /* Allowable mode bits on Win32. Using mode bits that are not supported on
37 * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
38 * so we simply remove them.
39 */
40 #define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
41
42 /* GetFinalPathNameByHandleW signature */
43 typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
44
45 /**
46 * Truncate or extend file.
47 *
48 * We now take a "git_off_t" rather than "long" because
49 * files may be longer than 2Gb.
50 */
51 int p_ftruncate(int fd, git_off_t size)
52 {
53 if (size < 0) {
54 errno = EINVAL;
55 return -1;
56 }
57
58 #if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
59 return ((_chsize_s(fd, size) == 0) ? 0 : -1);
60 #else
61 /* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
62 if (size > INT32_MAX) {
63 errno = EFBIG;
64 return -1;
65 }
66 return _chsize(fd, (long)size);
67 #endif
68 }
69
70 int p_mkdir(const char *path, mode_t mode)
71 {
72 git_win32_path buf;
73
74 GIT_UNUSED(mode);
75
76 if (git_win32_path_from_utf8(buf, path) < 0)
77 return -1;
78
79 return _wmkdir(buf);
80 }
81
82 int p_link(const char *old, const char *new)
83 {
84 GIT_UNUSED(old);
85 GIT_UNUSED(new);
86 errno = ENOSYS;
87 return -1;
88 }
89
90 int p_unlink(const char *path)
91 {
92 git_win32_path buf;
93 int error;
94
95 if (git_win32_path_from_utf8(buf, path) < 0)
96 return -1;
97
98 error = _wunlink(buf);
99
100 /* If the file could not be deleted because it was
101 * read-only, clear the bit and try again */
102 if (error == -1 && errno == EACCES) {
103 _wchmod(buf, 0666);
104 error = _wunlink(buf);
105 }
106
107 return error;
108 }
109
110 int p_fsync(int fd)
111 {
112 HANDLE fh = (HANDLE)_get_osfhandle(fd);
113
114 if (fh == INVALID_HANDLE_VALUE) {
115 errno = EBADF;
116 return -1;
117 }
118
119 if (!FlushFileBuffers(fh)) {
120 DWORD code = GetLastError();
121
122 if (code == ERROR_INVALID_HANDLE)
123 errno = EINVAL;
124 else
125 errno = EIO;
126
127 return -1;
128 }
129
130 return 0;
131 }
132
133 GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft)
134 {
135 long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
136 winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
137 winTime /= 10000000; /* Nano to seconds resolution */
138 return (time_t)winTime;
139 }
140
141 static bool path_is_volume(wchar_t *target, size_t target_len)
142 {
143 return (target_len && wcsncmp(target, L"\\??\\Volume{", 11) == 0);
144 }
145
146 /* On success, returns the length, in characters, of the path stored in dest.
147 * On failure, returns a negative value. */
148 static int readlink_w(
149 git_win32_path dest,
150 const git_win32_path path)
151 {
152 BYTE buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
153 GIT_REPARSE_DATA_BUFFER *reparse_buf = (GIT_REPARSE_DATA_BUFFER *)buf;
154 HANDLE handle = NULL;
155 DWORD ioctl_ret;
156 wchar_t *target;
157 size_t target_len;
158
159 int error = -1;
160
161 handle = CreateFileW(path, GENERIC_READ,
162 FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
163 FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
164
165 if (handle == INVALID_HANDLE_VALUE) {
166 errno = ENOENT;
167 return -1;
168 }
169
170 if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
171 reparse_buf, sizeof(buf), &ioctl_ret, NULL)) {
172 errno = EINVAL;
173 goto on_error;
174 }
175
176 switch (reparse_buf->ReparseTag) {
177 case IO_REPARSE_TAG_SYMLINK:
178 target = reparse_buf->SymbolicLinkReparseBuffer.PathBuffer +
179 (reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
180 target_len = reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
181 break;
182 case IO_REPARSE_TAG_MOUNT_POINT:
183 target = reparse_buf->MountPointReparseBuffer.PathBuffer +
184 (reparse_buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
185 target_len = reparse_buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
186 break;
187 default:
188 errno = EINVAL;
189 goto on_error;
190 }
191
192 if (path_is_volume(target, target_len)) {
193 /* This path is a reparse point that represents another volume mounted
194 * at this location, it is not a symbolic link our input was canonical.
195 */
196 errno = EINVAL;
197 error = -1;
198 } else if (target_len) {
199 /* The path may need to have a prefix removed. */
200 target_len = git_win32__canonicalize_path(target, target_len);
201
202 /* Need one additional character in the target buffer
203 * for the terminating NULL. */
204 if (GIT_WIN_PATH_UTF16 > target_len) {
205 wcscpy(dest, target);
206 error = (int)target_len;
207 }
208 }
209
210 on_error:
211 CloseHandle(handle);
212 return error;
213 }
214
215 #define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
216
217 static int lstat_w(
218 wchar_t *path,
219 struct stat *buf,
220 bool posix_enotdir)
221 {
222 WIN32_FILE_ATTRIBUTE_DATA fdata;
223
224 if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
225 int fMode = S_IREAD;
226
227 if (!buf)
228 return 0;
229
230 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
231 fMode |= S_IFDIR;
232 else
233 fMode |= S_IFREG;
234
235 if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
236 fMode |= S_IWRITE;
237
238 buf->st_ino = 0;
239 buf->st_gid = 0;
240 buf->st_uid = 0;
241 buf->st_nlink = 1;
242 buf->st_mode = (mode_t)fMode;
243 buf->st_size = ((git_off_t)fdata.nFileSizeHigh << 32) + fdata.nFileSizeLow;
244 buf->st_dev = buf->st_rdev = (_getdrive() - 1);
245 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
246 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
247 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
248
249 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
250 git_win32_path target;
251
252 if (readlink_w(target, path) >= 0) {
253 buf->st_mode = (buf->st_mode & ~S_IFMT) | S_IFLNK;
254
255 /* st_size gets the UTF-8 length of the target name, in bytes,
256 * not counting the NULL terminator */
257 if ((buf->st_size = git__utf16_to_8(NULL, 0, target)) < 0)
258 return -1;
259 }
260 }
261
262 return 0;
263 }
264
265 errno = ENOENT;
266
267 /* To match POSIX behavior, set ENOTDIR when any of the folders in the
268 * file path is a regular file, otherwise set ENOENT.
269 */
270 if (posix_enotdir) {
271 size_t path_len = wcslen(path);
272
273 /* scan up path until we find an existing item */
274 while (1) {
275 DWORD attrs;
276
277 /* remove last directory component */
278 for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
279
280 if (path_len <= 0)
281 break;
282
283 path[path_len] = L'\0';
284 attrs = GetFileAttributesW(path);
285
286 if (attrs != INVALID_FILE_ATTRIBUTES) {
287 if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
288 errno = ENOTDIR;
289 break;
290 }
291 }
292 }
293
294 return -1;
295 }
296
297 static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
298 {
299 git_win32_path path_w;
300 int len;
301
302 if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
303 return -1;
304
305 git_win32__path_trim_end(path_w, len);
306
307 return lstat_w(path_w, buf, posixly_correct);
308 }
309
310 int p_lstat(const char *filename, struct stat *buf)
311 {
312 return do_lstat(filename, buf, false);
313 }
314
315 int p_lstat_posixly(const char *filename, struct stat *buf)
316 {
317 return do_lstat(filename, buf, true);
318 }
319
320 int p_readlink(const char *path, char *buf, size_t bufsiz)
321 {
322 git_win32_path path_w, target_w;
323 git_win32_utf8_path target;
324 int len;
325
326 /* readlink(2) does not NULL-terminate the string written
327 * to the target buffer. Furthermore, the target buffer need
328 * not be large enough to hold the entire result. A truncated
329 * result should be written in this case. Since this truncation
330 * could occur in the middle of the encoding of a code point,
331 * we need to buffer the result on the stack. */
332
333 if (git_win32_path_from_utf8(path_w, path) < 0 ||
334 readlink_w(target_w, path_w) < 0 ||
335 (len = git_win32_path_to_utf8(target, target_w)) < 0)
336 return -1;
337
338 bufsiz = min((size_t)len, bufsiz);
339 memcpy(buf, target, bufsiz);
340
341 return (int)bufsiz;
342 }
343
344 int p_symlink(const char *old, const char *new)
345 {
346 /* Real symlinks on NTFS require admin privileges. Until this changes,
347 * libgit2 just creates a text file with the link target in the contents.
348 */
349 return git_futils_fake_symlink(old, new);
350 }
351
352 int p_open(const char *path, int flags, ...)
353 {
354 git_win32_path buf;
355 mode_t mode = 0;
356
357 if (git_win32_path_from_utf8(buf, path) < 0)
358 return -1;
359
360 if (flags & O_CREAT) {
361 va_list arg_list;
362
363 va_start(arg_list, flags);
364 mode = (mode_t)va_arg(arg_list, int);
365 va_end(arg_list);
366 }
367
368 return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode & WIN32_MODE_MASK);
369 }
370
371 int p_creat(const char *path, mode_t mode)
372 {
373 git_win32_path buf;
374
375 if (git_win32_path_from_utf8(buf, path) < 0)
376 return -1;
377
378 return _wopen(buf,
379 _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS,
380 mode & WIN32_MODE_MASK);
381 }
382
383 int p_getcwd(char *buffer_out, size_t size)
384 {
385 git_win32_path buf;
386 wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
387
388 if (!cwd)
389 return -1;
390
391 /* Convert the working directory back to UTF-8 */
392 if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
393 DWORD code = GetLastError();
394
395 if (code == ERROR_INSUFFICIENT_BUFFER)
396 errno = ERANGE;
397 else
398 errno = EINVAL;
399
400 return -1;
401 }
402
403 return 0;
404 }
405
406 /*
407 * Returns the address of the GetFinalPathNameByHandleW function.
408 * This function is available on Windows Vista and higher.
409 */
410 static PFGetFinalPathNameByHandleW get_fpnbyhandle(void)
411 {
412 static PFGetFinalPathNameByHandleW pFunc = NULL;
413 PFGetFinalPathNameByHandleW toReturn = pFunc;
414
415 if (!toReturn) {
416 HMODULE hModule = GetModuleHandleW(L"kernel32");
417
418 if (hModule)
419 toReturn = (PFGetFinalPathNameByHandleW)GetProcAddress(hModule, "GetFinalPathNameByHandleW");
420
421 pFunc = toReturn;
422 }
423
424 assert(toReturn);
425
426 return toReturn;
427 }
428
429 static int getfinalpath_w(
430 git_win32_path dest,
431 const wchar_t *path)
432 {
433 PFGetFinalPathNameByHandleW pgfp = get_fpnbyhandle();
434 HANDLE hFile;
435 DWORD dwChars;
436
437 if (!pgfp)
438 return -1;
439
440 /* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
441 * specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
442 * target of the link. */
443 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
444 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
445
446 if (INVALID_HANDLE_VALUE == hFile)
447 return -1;
448
449 /* Call GetFinalPathNameByHandle */
450 dwChars = pgfp(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
451 CloseHandle(hFile);
452
453 if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
454 return -1;
455
456 /* The path may be delivered to us with a prefix; canonicalize */
457 return (int)git_win32__canonicalize_path(dest, dwChars);
458 }
459
460 static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
461 {
462 git_win32_path target_w;
463
464 if (getfinalpath_w(target_w, path) < 0)
465 return -1;
466
467 return lstat_w(target_w, buf, false);
468 }
469
470 int p_stat(const char* path, struct stat* buf)
471 {
472 git_win32_path path_w;
473 int len;
474
475 if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
476 lstat_w(path_w, buf, false) < 0)
477 return -1;
478
479 /* The item is a symbolic link or mount point. No need to iterate
480 * to follow multiple links; use GetFinalPathNameFromHandle. */
481 if (S_ISLNK(buf->st_mode))
482 return follow_and_lstat_link(path_w, buf);
483
484 return 0;
485 }
486
487 int p_chdir(const char* path)
488 {
489 git_win32_path buf;
490
491 if (git_win32_path_from_utf8(buf, path) < 0)
492 return -1;
493
494 return _wchdir(buf);
495 }
496
497 int p_chmod(const char* path, mode_t mode)
498 {
499 git_win32_path buf;
500
501 if (git_win32_path_from_utf8(buf, path) < 0)
502 return -1;
503
504 return _wchmod(buf, mode);
505 }
506
507 int p_rmdir(const char* path)
508 {
509 git_win32_path buf;
510 int error;
511
512 if (git_win32_path_from_utf8(buf, path) < 0)
513 return -1;
514
515 error = _wrmdir(buf);
516
517 if (error == -1) {
518 switch (GetLastError()) {
519 /* _wrmdir() is documented to return EACCES if "A program has an open
520 * handle to the directory." This sounds like what everybody else calls
521 * EBUSY. Let's convert appropriate error codes.
522 */
523 case ERROR_SHARING_VIOLATION:
524 errno = EBUSY;
525 break;
526
527 /* This error can be returned when trying to rmdir an extant file. */
528 case ERROR_DIRECTORY:
529 errno = ENOTDIR;
530 break;
531 }
532 }
533
534 return error;
535 }
536
537 char *p_realpath(const char *orig_path, char *buffer)
538 {
539 git_win32_path orig_path_w, buffer_w;
540
541 if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
542 return NULL;
543
544 /* Note that if the path provided is a relative path, then the current directory
545 * is used to resolve the path -- which is a concurrency issue because the current
546 * directory is a process-wide variable. */
547 if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
548 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
549 errno = ENAMETOOLONG;
550 else
551 errno = EINVAL;
552
553 return NULL;
554 }
555
556 /* The path must exist. */
557 if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
558 errno = ENOENT;
559 return NULL;
560 }
561
562 if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
563 errno = ENOMEM;
564 return NULL;
565 }
566
567 /* Convert the path to UTF-8. If the caller provided a buffer, then it
568 * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
569 * then we may overflow. */
570 if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
571 return NULL;
572
573 git_path_mkposix(buffer);
574
575 return buffer;
576 }
577
578 int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
579 {
580 #if defined(_MSC_VER)
581 int len;
582
583 if (count == 0)
584 return _vscprintf(format, argptr);
585
586 #if _MSC_VER >= 1500
587 len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
588 #else
589 len = _vsnprintf(buffer, count, format, argptr);
590 #endif
591
592 if (len < 0)
593 return _vscprintf(format, argptr);
594
595 return len;
596 #else /* MinGW */
597 return vsnprintf(buffer, count, format, argptr);
598 #endif
599 }
600
601 int p_snprintf(char *buffer, size_t count, const char *format, ...)
602 {
603 va_list va;
604 int r;
605
606 va_start(va, format);
607 r = p_vsnprintf(buffer, count, format, va);
608 va_end(va);
609
610 return r;
611 }
612
613 /* TODO: wut? */
614 int p_mkstemp(char *tmp_path)
615 {
616 #if defined(_MSC_VER) && _MSC_VER >= 1500
617 if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
618 return -1;
619 #else
620 if (_mktemp(tmp_path) == NULL)
621 return -1;
622 #endif
623
624 return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
625 }
626
627 int p_access(const char* path, mode_t mode)
628 {
629 git_win32_path buf;
630
631 if (git_win32_path_from_utf8(buf, path) < 0)
632 return -1;
633
634 return _waccess(buf, mode & WIN32_MODE_MASK);
635 }
636
637 static int ensure_writable(wchar_t *fpath)
638 {
639 DWORD attrs;
640
641 attrs = GetFileAttributesW(fpath);
642 if (attrs == INVALID_FILE_ATTRIBUTES) {
643 if (GetLastError() == ERROR_FILE_NOT_FOUND)
644 return 0;
645
646 giterr_set(GITERR_OS, "failed to get attributes");
647 return -1;
648 }
649
650 if (!(attrs & FILE_ATTRIBUTE_READONLY))
651 return 0;
652
653 attrs &= ~FILE_ATTRIBUTE_READONLY;
654 if (!SetFileAttributesW(fpath, attrs)) {
655 giterr_set(GITERR_OS, "failed to set attributes");
656 return -1;
657 }
658
659 return 0;
660 }
661
662 int p_rename(const char *from, const char *to)
663 {
664 git_win32_path wfrom;
665 git_win32_path wto;
666 int rename_tries;
667 int rename_succeeded;
668 int error;
669
670 if (git_win32_path_from_utf8(wfrom, from) < 0 ||
671 git_win32_path_from_utf8(wto, to) < 0)
672 return -1;
673
674 /* wait up to 50ms if file is locked by another thread or process */
675 rename_tries = 0;
676 rename_succeeded = 0;
677 while (rename_tries < 10) {
678 if (ensure_writable(wto) == 0 &&
679 MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
680 rename_succeeded = 1;
681 break;
682 }
683
684 error = GetLastError();
685 if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) {
686 Sleep(5);
687 rename_tries++;
688 } else
689 break;
690 }
691
692 return rename_succeeded ? 0 : -1;
693 }
694
695 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
696 {
697 if ((size_t)((int)length) != length)
698 return -1; /* giterr_set will be done by caller */
699
700 return recv(socket, buffer, (int)length, flags);
701 }
702
703 int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
704 {
705 if ((size_t)((int)length) != length)
706 return -1; /* giterr_set will be done by caller */
707
708 return send(socket, buffer, (int)length, flags);
709 }
710
711 /**
712 * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
713 * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
714 */
715 struct tm *
716 p_localtime_r (const time_t *timer, struct tm *result)
717 {
718 struct tm *local_result;
719 local_result = localtime (timer);
720
721 if (local_result == NULL || result == NULL)
722 return NULL;
723
724 memcpy (result, local_result, sizeof (struct tm));
725 return result;
726 }
727 struct tm *
728 p_gmtime_r (const time_t *timer, struct tm *result)
729 {
730 struct tm *local_result;
731 local_result = gmtime (timer);
732
733 if (local_result == NULL || result == NULL)
734 return NULL;
735
736 memcpy (result, local_result, sizeof (struct tm));
737 return result;
738 }
739
740 int p_inet_pton(int af, const char *src, void *dst)
741 {
742 struct sockaddr_storage sin;
743 void *addr;
744 int sin_len = sizeof(struct sockaddr_storage), addr_len;
745 int error = 0;
746
747 if (af == AF_INET) {
748 addr = &((struct sockaddr_in *)&sin)->sin_addr;
749 addr_len = sizeof(struct in_addr);
750 } else if (af == AF_INET6) {
751 addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
752 addr_len = sizeof(struct in6_addr);
753 } else {
754 errno = EAFNOSUPPORT;
755 return -1;
756 }
757
758 if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
759 memcpy(dst, addr, addr_len);
760 return 1;
761 }
762
763 switch(WSAGetLastError()) {
764 case WSAEINVAL:
765 return 0;
766 case WSAEFAULT:
767 errno = ENOSPC;
768 return -1;
769 case WSA_NOT_ENOUGH_MEMORY:
770 errno = ENOMEM;
771 return -1;
772 }
773
774 errno = EINVAL;
775 return -1;
776 }