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