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