]> git.proxmox.com Git - libgit2.git/blob - src/win32/posix_w32.c
f51e1e546abb0378e452dcd5a9943edf89c7da33
[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
8 #include "common.h"
9
10 #include "../posix.h"
11 #include "../fileops.h"
12 #include "path.h"
13 #include "path_w32.h"
14 #include "utf-conv.h"
15 #include "repository.h"
16 #include "reparse.h"
17 #include "global.h"
18 #include "buffer.h"
19 #include <errno.h>
20 #include <io.h>
21 #include <fcntl.h>
22 #include <ws2tcpip.h>
23
24 #ifndef FILE_NAME_NORMALIZED
25 # define FILE_NAME_NORMALIZED 0
26 #endif
27
28 #ifndef IO_REPARSE_TAG_SYMLINK
29 #define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
30 #endif
31
32 /* Allowable mode bits on Win32. Using mode bits that are not supported on
33 * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
34 * so we simply remove them.
35 */
36 #define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
37
38 /* GetFinalPathNameByHandleW signature */
39 typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
40
41 unsigned long git_win32__createfile_sharemode =
42 FILE_SHARE_READ | FILE_SHARE_WRITE;
43 int git_win32__retries = 10;
44
45 GIT_INLINE(void) set_errno(void)
46 {
47 switch (GetLastError()) {
48 case ERROR_FILE_NOT_FOUND:
49 case ERROR_PATH_NOT_FOUND:
50 case ERROR_INVALID_DRIVE:
51 case ERROR_NO_MORE_FILES:
52 case ERROR_BAD_NETPATH:
53 case ERROR_BAD_NET_NAME:
54 case ERROR_BAD_PATHNAME:
55 case ERROR_FILENAME_EXCED_RANGE:
56 errno = ENOENT;
57 break;
58 case ERROR_BAD_ENVIRONMENT:
59 errno = E2BIG;
60 break;
61 case ERROR_BAD_FORMAT:
62 case ERROR_INVALID_STARTING_CODESEG:
63 case ERROR_INVALID_STACKSEG:
64 case ERROR_INVALID_MODULETYPE:
65 case ERROR_INVALID_EXE_SIGNATURE:
66 case ERROR_EXE_MARKED_INVALID:
67 case ERROR_BAD_EXE_FORMAT:
68 case ERROR_ITERATED_DATA_EXCEEDS_64k:
69 case ERROR_INVALID_MINALLOCSIZE:
70 case ERROR_DYNLINK_FROM_INVALID_RING:
71 case ERROR_IOPL_NOT_ENABLED:
72 case ERROR_INVALID_SEGDPL:
73 case ERROR_AUTODATASEG_EXCEEDS_64k:
74 case ERROR_RING2SEG_MUST_BE_MOVABLE:
75 case ERROR_RELOC_CHAIN_XEEDS_SEGLIM:
76 case ERROR_INFLOOP_IN_RELOC_CHAIN:
77 errno = ENOEXEC;
78 break;
79 case ERROR_INVALID_HANDLE:
80 case ERROR_INVALID_TARGET_HANDLE:
81 case ERROR_DIRECT_ACCESS_HANDLE:
82 errno = EBADF;
83 break;
84 case ERROR_WAIT_NO_CHILDREN:
85 case ERROR_CHILD_NOT_COMPLETE:
86 errno = ECHILD;
87 break;
88 case ERROR_NO_PROC_SLOTS:
89 case ERROR_MAX_THRDS_REACHED:
90 case ERROR_NESTING_NOT_ALLOWED:
91 errno = EAGAIN;
92 break;
93 case ERROR_ARENA_TRASHED:
94 case ERROR_NOT_ENOUGH_MEMORY:
95 case ERROR_INVALID_BLOCK:
96 case ERROR_NOT_ENOUGH_QUOTA:
97 errno = ENOMEM;
98 break;
99 case ERROR_ACCESS_DENIED:
100 case ERROR_CURRENT_DIRECTORY:
101 case ERROR_WRITE_PROTECT:
102 case ERROR_BAD_UNIT:
103 case ERROR_NOT_READY:
104 case ERROR_BAD_COMMAND:
105 case ERROR_CRC:
106 case ERROR_BAD_LENGTH:
107 case ERROR_SEEK:
108 case ERROR_NOT_DOS_DISK:
109 case ERROR_SECTOR_NOT_FOUND:
110 case ERROR_OUT_OF_PAPER:
111 case ERROR_WRITE_FAULT:
112 case ERROR_READ_FAULT:
113 case ERROR_GEN_FAILURE:
114 case ERROR_SHARING_VIOLATION:
115 case ERROR_LOCK_VIOLATION:
116 case ERROR_WRONG_DISK:
117 case ERROR_SHARING_BUFFER_EXCEEDED:
118 case ERROR_NETWORK_ACCESS_DENIED:
119 case ERROR_CANNOT_MAKE:
120 case ERROR_FAIL_I24:
121 case ERROR_DRIVE_LOCKED:
122 case ERROR_SEEK_ON_DEVICE:
123 case ERROR_NOT_LOCKED:
124 case ERROR_LOCK_FAILED:
125 errno = EACCES;
126 break;
127 case ERROR_FILE_EXISTS:
128 case ERROR_ALREADY_EXISTS:
129 errno = EEXIST;
130 break;
131 case ERROR_NOT_SAME_DEVICE:
132 errno = EXDEV;
133 break;
134 case ERROR_INVALID_FUNCTION:
135 case ERROR_INVALID_ACCESS:
136 case ERROR_INVALID_DATA:
137 case ERROR_INVALID_PARAMETER:
138 case ERROR_NEGATIVE_SEEK:
139 errno = EINVAL;
140 break;
141 case ERROR_TOO_MANY_OPEN_FILES:
142 errno = EMFILE;
143 break;
144 case ERROR_DISK_FULL:
145 errno = ENOSPC;
146 break;
147 case ERROR_BROKEN_PIPE:
148 errno = EPIPE;
149 break;
150 case ERROR_DIR_NOT_EMPTY:
151 errno = ENOTEMPTY;
152 break;
153 default:
154 errno = EINVAL;
155 }
156 }
157
158 GIT_INLINE(bool) last_error_retryable(void)
159 {
160 int os_error = GetLastError();
161
162 return (os_error == ERROR_SHARING_VIOLATION ||
163 os_error == ERROR_ACCESS_DENIED);
164 }
165
166 #define do_with_retries(fn, remediation) \
167 do { \
168 int __retry, __ret; \
169 for (__retry = git_win32__retries; __retry; __retry--) { \
170 if ((__ret = (fn)) != GIT_RETRY) \
171 return __ret; \
172 if (__retry > 1 && (__ret = (remediation)) != 0) { \
173 if (__ret == GIT_RETRY) \
174 continue; \
175 return __ret; \
176 } \
177 Sleep(5); \
178 } \
179 return -1; \
180 } while (0) \
181
182 static int ensure_writable(wchar_t *path)
183 {
184 DWORD attrs;
185
186 if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES)
187 goto on_error;
188
189 if ((attrs & FILE_ATTRIBUTE_READONLY) == 0)
190 return 0;
191
192 if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
193 goto on_error;
194
195 return GIT_RETRY;
196
197 on_error:
198 set_errno();
199 return -1;
200 }
201
202 /**
203 * Truncate or extend file.
204 *
205 * We now take a "git_off_t" rather than "long" because
206 * files may be longer than 2Gb.
207 */
208 int p_ftruncate(int fd, git_off_t size)
209 {
210 if (size < 0) {
211 errno = EINVAL;
212 return -1;
213 }
214
215 #if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
216 return ((_chsize_s(fd, size) == 0) ? 0 : -1);
217 #else
218 /* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
219 if (size > INT32_MAX) {
220 errno = EFBIG;
221 return -1;
222 }
223 return _chsize(fd, (long)size);
224 #endif
225 }
226
227 int p_mkdir(const char *path, mode_t mode)
228 {
229 git_win32_path buf;
230
231 GIT_UNUSED(mode);
232
233 if (git_win32_path_from_utf8(buf, path) < 0)
234 return -1;
235
236 return _wmkdir(buf);
237 }
238
239 int p_link(const char *old, const char *new)
240 {
241 GIT_UNUSED(old);
242 GIT_UNUSED(new);
243 errno = ENOSYS;
244 return -1;
245 }
246
247 GIT_INLINE(int) unlink_once(const wchar_t *path)
248 {
249 if (DeleteFileW(path))
250 return 0;
251
252 if (last_error_retryable())
253 return GIT_RETRY;
254
255 set_errno();
256 return -1;
257 }
258
259 int p_unlink(const char *path)
260 {
261 git_win32_path wpath;
262
263 if (git_win32_path_from_utf8(wpath, path) < 0)
264 return -1;
265
266 do_with_retries(unlink_once(wpath), ensure_writable(wpath));
267 }
268
269 int p_fsync(int fd)
270 {
271 HANDLE fh = (HANDLE)_get_osfhandle(fd);
272
273 p_fsync__cnt++;
274
275 if (fh == INVALID_HANDLE_VALUE) {
276 errno = EBADF;
277 return -1;
278 }
279
280 if (!FlushFileBuffers(fh)) {
281 DWORD code = GetLastError();
282
283 if (code == ERROR_INVALID_HANDLE)
284 errno = EINVAL;
285 else
286 errno = EIO;
287
288 return -1;
289 }
290
291 return 0;
292 }
293
294 #define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
295
296 static int lstat_w(
297 wchar_t *path,
298 struct stat *buf,
299 bool posix_enotdir)
300 {
301 WIN32_FILE_ATTRIBUTE_DATA fdata;
302
303 if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
304 if (!buf)
305 return 0;
306
307 return git_win32__file_attribute_to_stat(buf, &fdata, path);
308 }
309
310 switch (GetLastError()) {
311 case ERROR_ACCESS_DENIED:
312 errno = EACCES;
313 break;
314 default:
315 errno = ENOENT;
316 break;
317 }
318
319 /* To match POSIX behavior, set ENOTDIR when any of the folders in the
320 * file path is a regular file, otherwise set ENOENT.
321 */
322 if (errno == ENOENT && posix_enotdir) {
323 size_t path_len = wcslen(path);
324
325 /* scan up path until we find an existing item */
326 while (1) {
327 DWORD attrs;
328
329 /* remove last directory component */
330 for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
331
332 if (path_len <= 0)
333 break;
334
335 path[path_len] = L'\0';
336 attrs = GetFileAttributesW(path);
337
338 if (attrs != INVALID_FILE_ATTRIBUTES) {
339 if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
340 errno = ENOTDIR;
341 break;
342 }
343 }
344 }
345
346 return -1;
347 }
348
349 static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
350 {
351 git_win32_path path_w;
352 int len;
353
354 if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
355 return -1;
356
357 git_win32__path_trim_end(path_w, len);
358
359 return lstat_w(path_w, buf, posixly_correct);
360 }
361
362 int p_lstat(const char *filename, struct stat *buf)
363 {
364 return do_lstat(filename, buf, false);
365 }
366
367 int p_lstat_posixly(const char *filename, struct stat *buf)
368 {
369 return do_lstat(filename, buf, true);
370 }
371
372 int p_readlink(const char *path, char *buf, size_t bufsiz)
373 {
374 git_win32_path path_w, target_w;
375 git_win32_utf8_path target;
376 int len;
377
378 /* readlink(2) does not NULL-terminate the string written
379 * to the target buffer. Furthermore, the target buffer need
380 * not be large enough to hold the entire result. A truncated
381 * result should be written in this case. Since this truncation
382 * could occur in the middle of the encoding of a code point,
383 * we need to buffer the result on the stack. */
384
385 if (git_win32_path_from_utf8(path_w, path) < 0 ||
386 git_win32_path_readlink_w(target_w, path_w) < 0 ||
387 (len = git_win32_path_to_utf8(target, target_w)) < 0)
388 return -1;
389
390 bufsiz = min((size_t)len, bufsiz);
391 memcpy(buf, target, bufsiz);
392
393 return (int)bufsiz;
394 }
395
396 int p_symlink(const char *old, const char *new)
397 {
398 /* Real symlinks on NTFS require admin privileges. Until this changes,
399 * libgit2 just creates a text file with the link target in the contents.
400 */
401 return git_futils_fake_symlink(old, new);
402 }
403
404 struct open_opts {
405 DWORD access;
406 DWORD sharing;
407 SECURITY_ATTRIBUTES security;
408 DWORD creation_disposition;
409 DWORD attributes;
410 int osf_flags;
411 };
412
413 GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode)
414 {
415 memset(opts, 0, sizeof(struct open_opts));
416
417 switch (flags & (O_WRONLY | O_RDWR)) {
418 case O_WRONLY:
419 opts->access = GENERIC_WRITE;
420 break;
421 case O_RDWR:
422 opts->access = GENERIC_READ | GENERIC_WRITE;
423 break;
424 default:
425 opts->access = GENERIC_READ;
426 break;
427 }
428
429 opts->sharing = (DWORD)git_win32__createfile_sharemode;
430
431 switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) {
432 case O_CREAT | O_EXCL:
433 case O_CREAT | O_TRUNC | O_EXCL:
434 opts->creation_disposition = CREATE_NEW;
435 break;
436 case O_CREAT | O_TRUNC:
437 opts->creation_disposition = CREATE_ALWAYS;
438 break;
439 case O_TRUNC:
440 opts->creation_disposition = TRUNCATE_EXISTING;
441 break;
442 case O_CREAT:
443 opts->creation_disposition = OPEN_ALWAYS;
444 break;
445 default:
446 opts->creation_disposition = OPEN_EXISTING;
447 break;
448 }
449
450 opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ?
451 FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
452 opts->osf_flags = flags & (O_RDONLY | O_APPEND);
453
454 opts->security.nLength = sizeof(SECURITY_ATTRIBUTES);
455 opts->security.lpSecurityDescriptor = NULL;
456 opts->security.bInheritHandle = 0;
457 }
458
459 GIT_INLINE(int) open_once(
460 const wchar_t *path,
461 struct open_opts *opts)
462 {
463 int fd;
464
465 HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
466 &opts->security, opts->creation_disposition, opts->attributes, 0);
467
468 if (handle == INVALID_HANDLE_VALUE) {
469 if (last_error_retryable())
470 return GIT_RETRY;
471
472 set_errno();
473 return -1;
474 }
475
476 if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
477 CloseHandle(handle);
478
479 return fd;
480 }
481
482 int p_open(const char *path, int flags, ...)
483 {
484 git_win32_path wpath;
485 mode_t mode = 0;
486 struct open_opts opts = {0};
487
488 if (git_win32_path_from_utf8(wpath, path) < 0)
489 return -1;
490
491 if (flags & O_CREAT) {
492 va_list arg_list;
493
494 va_start(arg_list, flags);
495 mode = (mode_t)va_arg(arg_list, int);
496 va_end(arg_list);
497 }
498
499 open_opts_from_posix(&opts, flags, mode);
500
501 do_with_retries(
502 open_once(wpath, &opts),
503 0);
504 }
505
506 int p_creat(const char *path, mode_t mode)
507 {
508 return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
509 }
510
511 int p_utimes(const char *path, const struct p_timeval times[2])
512 {
513 git_win32_path wpath;
514 int fd, error;
515 DWORD attrs_orig, attrs_new = 0;
516 struct open_opts opts = { 0 };
517
518 if (git_win32_path_from_utf8(wpath, path) < 0)
519 return -1;
520
521 attrs_orig = GetFileAttributesW(wpath);
522
523 if (attrs_orig & FILE_ATTRIBUTE_READONLY) {
524 attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY;
525
526 if (!SetFileAttributesW(wpath, attrs_new)) {
527 giterr_set(GITERR_OS, "failed to set attributes");
528 return -1;
529 }
530 }
531
532 open_opts_from_posix(&opts, O_RDWR, 0);
533
534 if ((fd = open_once(wpath, &opts)) < 0) {
535 error = -1;
536 goto done;
537 }
538
539 error = p_futimes(fd, times);
540 close(fd);
541
542 done:
543 if (attrs_orig != attrs_new) {
544 DWORD os_error = GetLastError();
545 SetFileAttributesW(wpath, attrs_orig);
546 SetLastError(os_error);
547 }
548
549 return error;
550 }
551
552 int p_futimes(int fd, const struct p_timeval times[2])
553 {
554 HANDLE handle;
555 FILETIME atime = { 0 }, mtime = { 0 };
556
557 if (times == NULL) {
558 SYSTEMTIME st;
559
560 GetSystemTime(&st);
561 SystemTimeToFileTime(&st, &atime);
562 SystemTimeToFileTime(&st, &mtime);
563 }
564 else {
565 git_win32__timeval_to_filetime(&atime, times[0]);
566 git_win32__timeval_to_filetime(&mtime, times[1]);
567 }
568
569 if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
570 return -1;
571
572 if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
573 return -1;
574
575 return 0;
576 }
577
578 int p_getcwd(char *buffer_out, size_t size)
579 {
580 git_win32_path buf;
581 wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
582
583 if (!cwd)
584 return -1;
585
586 /* Convert the working directory back to UTF-8 */
587 if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
588 DWORD code = GetLastError();
589
590 if (code == ERROR_INSUFFICIENT_BUFFER)
591 errno = ERANGE;
592 else
593 errno = EINVAL;
594
595 return -1;
596 }
597
598 return 0;
599 }
600
601 /*
602 * Returns the address of the GetFinalPathNameByHandleW function.
603 * This function is available on Windows Vista and higher.
604 */
605 static PFGetFinalPathNameByHandleW get_fpnbyhandle(void)
606 {
607 static PFGetFinalPathNameByHandleW pFunc = NULL;
608 PFGetFinalPathNameByHandleW toReturn = pFunc;
609
610 if (!toReturn) {
611 HMODULE hModule = GetModuleHandleW(L"kernel32");
612
613 if (hModule)
614 toReturn = (PFGetFinalPathNameByHandleW)GetProcAddress(hModule, "GetFinalPathNameByHandleW");
615
616 pFunc = toReturn;
617 }
618
619 assert(toReturn);
620
621 return toReturn;
622 }
623
624 static int getfinalpath_w(
625 git_win32_path dest,
626 const wchar_t *path)
627 {
628 PFGetFinalPathNameByHandleW pgfp = get_fpnbyhandle();
629 HANDLE hFile;
630 DWORD dwChars;
631
632 if (!pgfp)
633 return -1;
634
635 /* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
636 * specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
637 * target of the link. */
638 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
639 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
640
641 if (INVALID_HANDLE_VALUE == hFile)
642 return -1;
643
644 /* Call GetFinalPathNameByHandle */
645 dwChars = pgfp(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
646 CloseHandle(hFile);
647
648 if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
649 return -1;
650
651 /* The path may be delivered to us with a prefix; canonicalize */
652 return (int)git_win32__canonicalize_path(dest, dwChars);
653 }
654
655 static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
656 {
657 git_win32_path target_w;
658
659 if (getfinalpath_w(target_w, path) < 0)
660 return -1;
661
662 return lstat_w(target_w, buf, false);
663 }
664
665 int p_fstat(int fd, struct stat *buf)
666 {
667 BY_HANDLE_FILE_INFORMATION fhInfo;
668
669 HANDLE fh = (HANDLE)_get_osfhandle(fd);
670
671 if (fh == INVALID_HANDLE_VALUE ||
672 !GetFileInformationByHandle(fh, &fhInfo)) {
673 errno = EBADF;
674 return -1;
675 }
676
677 git_win32__file_information_to_stat(buf, &fhInfo);
678 return 0;
679 }
680
681 int p_stat(const char* path, struct stat* buf)
682 {
683 git_win32_path path_w;
684 int len;
685
686 if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
687 lstat_w(path_w, buf, false) < 0)
688 return -1;
689
690 /* The item is a symbolic link or mount point. No need to iterate
691 * to follow multiple links; use GetFinalPathNameFromHandle. */
692 if (S_ISLNK(buf->st_mode))
693 return follow_and_lstat_link(path_w, buf);
694
695 return 0;
696 }
697
698 int p_chdir(const char* path)
699 {
700 git_win32_path buf;
701
702 if (git_win32_path_from_utf8(buf, path) < 0)
703 return -1;
704
705 return _wchdir(buf);
706 }
707
708 int p_chmod(const char* path, mode_t mode)
709 {
710 git_win32_path buf;
711
712 if (git_win32_path_from_utf8(buf, path) < 0)
713 return -1;
714
715 return _wchmod(buf, mode);
716 }
717
718 int p_rmdir(const char* path)
719 {
720 git_win32_path buf;
721 int error;
722
723 if (git_win32_path_from_utf8(buf, path) < 0)
724 return -1;
725
726 error = _wrmdir(buf);
727
728 if (error == -1) {
729 switch (GetLastError()) {
730 /* _wrmdir() is documented to return EACCES if "A program has an open
731 * handle to the directory." This sounds like what everybody else calls
732 * EBUSY. Let's convert appropriate error codes.
733 */
734 case ERROR_SHARING_VIOLATION:
735 errno = EBUSY;
736 break;
737
738 /* This error can be returned when trying to rmdir an extant file. */
739 case ERROR_DIRECTORY:
740 errno = ENOTDIR;
741 break;
742 }
743 }
744
745 return error;
746 }
747
748 char *p_realpath(const char *orig_path, char *buffer)
749 {
750 git_win32_path orig_path_w, buffer_w;
751
752 if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
753 return NULL;
754
755 /* Note that if the path provided is a relative path, then the current directory
756 * is used to resolve the path -- which is a concurrency issue because the current
757 * directory is a process-wide variable. */
758 if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
759 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
760 errno = ENAMETOOLONG;
761 else
762 errno = EINVAL;
763
764 return NULL;
765 }
766
767 /* The path must exist. */
768 if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
769 errno = ENOENT;
770 return NULL;
771 }
772
773 if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
774 errno = ENOMEM;
775 return NULL;
776 }
777
778 /* Convert the path to UTF-8. If the caller provided a buffer, then it
779 * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
780 * then we may overflow. */
781 if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
782 return NULL;
783
784 git_path_mkposix(buffer);
785
786 return buffer;
787 }
788
789 int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
790 {
791 #if defined(_MSC_VER)
792 int len;
793
794 if (count == 0)
795 return _vscprintf(format, argptr);
796
797 #if _MSC_VER >= 1500
798 len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
799 #else
800 len = _vsnprintf(buffer, count, format, argptr);
801 #endif
802
803 if (len < 0)
804 return _vscprintf(format, argptr);
805
806 return len;
807 #else /* MinGW */
808 return vsnprintf(buffer, count, format, argptr);
809 #endif
810 }
811
812 int p_snprintf(char *buffer, size_t count, const char *format, ...)
813 {
814 va_list va;
815 int r;
816
817 va_start(va, format);
818 r = p_vsnprintf(buffer, count, format, va);
819 va_end(va);
820
821 return r;
822 }
823
824 /* TODO: wut? */
825 int p_mkstemp(char *tmp_path)
826 {
827 #if defined(_MSC_VER) && _MSC_VER >= 1500
828 if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
829 return -1;
830 #else
831 if (_mktemp(tmp_path) == NULL)
832 return -1;
833 #endif
834
835 return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
836 }
837
838 int p_access(const char* path, mode_t mode)
839 {
840 git_win32_path buf;
841
842 if (git_win32_path_from_utf8(buf, path) < 0)
843 return -1;
844
845 return _waccess(buf, mode & WIN32_MODE_MASK);
846 }
847
848 GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to)
849 {
850 if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
851 return 0;
852
853 if (last_error_retryable())
854 return GIT_RETRY;
855
856 set_errno();
857 return -1;
858 }
859
860 int p_rename(const char *from, const char *to)
861 {
862 git_win32_path wfrom, wto;
863
864 if (git_win32_path_from_utf8(wfrom, from) < 0 ||
865 git_win32_path_from_utf8(wto, to) < 0)
866 return -1;
867
868 do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
869 }
870
871 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
872 {
873 if ((size_t)((int)length) != length)
874 return -1; /* giterr_set will be done by caller */
875
876 return recv(socket, buffer, (int)length, flags);
877 }
878
879 int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
880 {
881 if ((size_t)((int)length) != length)
882 return -1; /* giterr_set will be done by caller */
883
884 return send(socket, buffer, (int)length, flags);
885 }
886
887 /**
888 * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
889 * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
890 */
891 struct tm *
892 p_localtime_r (const time_t *timer, struct tm *result)
893 {
894 struct tm *local_result;
895 local_result = localtime (timer);
896
897 if (local_result == NULL || result == NULL)
898 return NULL;
899
900 memcpy (result, local_result, sizeof (struct tm));
901 return result;
902 }
903 struct tm *
904 p_gmtime_r (const time_t *timer, struct tm *result)
905 {
906 struct tm *local_result;
907 local_result = gmtime (timer);
908
909 if (local_result == NULL || result == NULL)
910 return NULL;
911
912 memcpy (result, local_result, sizeof (struct tm));
913 return result;
914 }
915
916 int p_inet_pton(int af, const char *src, void *dst)
917 {
918 struct sockaddr_storage sin;
919 void *addr;
920 int sin_len = sizeof(struct sockaddr_storage), addr_len;
921 int error = 0;
922
923 if (af == AF_INET) {
924 addr = &((struct sockaddr_in *)&sin)->sin_addr;
925 addr_len = sizeof(struct in_addr);
926 } else if (af == AF_INET6) {
927 addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
928 addr_len = sizeof(struct in6_addr);
929 } else {
930 errno = EAFNOSUPPORT;
931 return -1;
932 }
933
934 if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
935 memcpy(dst, addr, addr_len);
936 return 1;
937 }
938
939 switch(WSAGetLastError()) {
940 case WSAEINVAL:
941 return 0;
942 case WSAEFAULT:
943 errno = ENOSPC;
944 return -1;
945 case WSA_NOT_ENOUGH_MEMORY:
946 errno = ENOMEM;
947 return -1;
948 }
949
950 errno = EINVAL;
951 return -1;
952 }