]> git.proxmox.com Git - libgit2.git/blob - src/win32/posix_w32.c
29641bdaff13593ee1d1f20b303509b45f41b30f
[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 "../futils.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, off64_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 DWORD error;
255
256 if (DeleteFileW(path))
257 return 0;
258
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))
264 goto out;
265
266 if (RemoveDirectoryW(path))
267 return 0;
268 }
269
270 out:
271 SetLastError(error);
272
273 if (last_error_retryable())
274 return GIT_RETRY;
275
276 set_errno();
277 return -1;
278 }
279
280 int p_unlink(const char *path)
281 {
282 git_win32_path wpath;
283
284 if (git_win32_path_from_utf8(wpath, path) < 0)
285 return -1;
286
287 do_with_retries(unlink_once(wpath), ensure_writable(wpath));
288 }
289
290 int p_fsync(int fd)
291 {
292 HANDLE fh = (HANDLE)_get_osfhandle(fd);
293
294 p_fsync__cnt++;
295
296 if (fh == INVALID_HANDLE_VALUE) {
297 errno = EBADF;
298 return -1;
299 }
300
301 if (!FlushFileBuffers(fh)) {
302 DWORD code = GetLastError();
303
304 if (code == ERROR_INVALID_HANDLE)
305 errno = EINVAL;
306 else
307 errno = EIO;
308
309 return -1;
310 }
311
312 return 0;
313 }
314
315 #define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
316
317 static int lstat_w(
318 wchar_t *path,
319 struct stat *buf,
320 bool posix_enotdir)
321 {
322 WIN32_FILE_ATTRIBUTE_DATA fdata;
323
324 if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
325 if (!buf)
326 return 0;
327
328 return git_win32__file_attribute_to_stat(buf, &fdata, path);
329 }
330
331 switch (GetLastError()) {
332 case ERROR_ACCESS_DENIED:
333 errno = EACCES;
334 break;
335 default:
336 errno = ENOENT;
337 break;
338 }
339
340 /* To match POSIX behavior, set ENOTDIR when any of the folders in the
341 * file path is a regular file, otherwise set ENOENT.
342 */
343 if (errno == ENOENT && posix_enotdir) {
344 size_t path_len = wcslen(path);
345
346 /* scan up path until we find an existing item */
347 while (1) {
348 DWORD attrs;
349
350 /* remove last directory component */
351 for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
352
353 if (path_len <= 0)
354 break;
355
356 path[path_len] = L'\0';
357 attrs = GetFileAttributesW(path);
358
359 if (attrs != INVALID_FILE_ATTRIBUTES) {
360 if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
361 errno = ENOTDIR;
362 break;
363 }
364 }
365 }
366
367 return -1;
368 }
369
370 static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
371 {
372 git_win32_path path_w;
373 int len;
374
375 if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
376 return -1;
377
378 git_win32_path_trim_end(path_w, len);
379
380 return lstat_w(path_w, buf, posixly_correct);
381 }
382
383 int p_lstat(const char *filename, struct stat *buf)
384 {
385 return do_lstat(filename, buf, false);
386 }
387
388 int p_lstat_posixly(const char *filename, struct stat *buf)
389 {
390 return do_lstat(filename, buf, true);
391 }
392
393 int p_readlink(const char *path, char *buf, size_t bufsiz)
394 {
395 git_win32_path path_w, target_w;
396 git_win32_utf8_path target;
397 int len;
398
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. */
405
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)
409 return -1;
410
411 bufsiz = min((size_t)len, bufsiz);
412 memcpy(buf, target, bufsiz);
413
414 return (int)bufsiz;
415 }
416
417 static bool target_is_dir(const char *target, const char *path)
418 {
419 git_buf resolved = GIT_BUF_INIT;
420 git_win32_path resolved_w;
421 bool isdir = true;
422
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)
428 goto out;
429
430 isdir = GetFileAttributesW(resolved_w) & FILE_ATTRIBUTE_DIRECTORY;
431
432 out:
433 git_buf_dispose(&resolved);
434 return isdir;
435 }
436
437 int p_symlink(const char *target, const char *path)
438 {
439 git_win32_path target_w, path_w;
440 DWORD dwFlags;
441
442 /*
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.
448 */
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)
452 return -1;
453
454 dwFlags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
455 if (target_is_dir(target, path))
456 dwFlags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
457
458 if (!CreateSymbolicLinkW(path_w, target_w, dwFlags))
459 return -1;
460
461 return 0;
462 }
463
464 struct open_opts {
465 DWORD access;
466 DWORD sharing;
467 SECURITY_ATTRIBUTES security;
468 DWORD creation_disposition;
469 DWORD attributes;
470 int osf_flags;
471 };
472
473 GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode)
474 {
475 memset(opts, 0, sizeof(struct open_opts));
476
477 switch (flags & (O_WRONLY | O_RDWR)) {
478 case O_WRONLY:
479 opts->access = GENERIC_WRITE;
480 break;
481 case O_RDWR:
482 opts->access = GENERIC_READ | GENERIC_WRITE;
483 break;
484 default:
485 opts->access = GENERIC_READ;
486 break;
487 }
488
489 opts->sharing = (DWORD)git_win32__createfile_sharemode;
490
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;
495 break;
496 case O_CREAT | O_TRUNC:
497 opts->creation_disposition = CREATE_ALWAYS;
498 break;
499 case O_TRUNC:
500 opts->creation_disposition = TRUNCATE_EXISTING;
501 break;
502 case O_CREAT:
503 opts->creation_disposition = OPEN_ALWAYS;
504 break;
505 default:
506 opts->creation_disposition = OPEN_EXISTING;
507 break;
508 }
509
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);
513
514 opts->security.nLength = sizeof(SECURITY_ATTRIBUTES);
515 opts->security.lpSecurityDescriptor = NULL;
516 opts->security.bInheritHandle = 0;
517 }
518
519 GIT_INLINE(int) open_once(
520 const wchar_t *path,
521 struct open_opts *opts)
522 {
523 int fd;
524
525 HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
526 &opts->security, opts->creation_disposition, opts->attributes, 0);
527
528 if (handle == INVALID_HANDLE_VALUE) {
529 if (last_error_retryable())
530 return GIT_RETRY;
531
532 set_errno();
533 return -1;
534 }
535
536 if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
537 CloseHandle(handle);
538
539 return fd;
540 }
541
542 int p_open(const char *path, int flags, ...)
543 {
544 git_win32_path wpath;
545 mode_t mode = 0;
546 struct open_opts opts = {0};
547
548 if (git_win32_path_from_utf8(wpath, path) < 0)
549 return -1;
550
551 if (flags & O_CREAT) {
552 va_list arg_list;
553
554 va_start(arg_list, flags);
555 mode = (mode_t)va_arg(arg_list, int);
556 va_end(arg_list);
557 }
558
559 open_opts_from_posix(&opts, flags, mode);
560
561 do_with_retries(
562 open_once(wpath, &opts),
563 0);
564 }
565
566 int p_creat(const char *path, mode_t mode)
567 {
568 return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
569 }
570
571 int p_utimes(const char *path, const struct p_timeval times[2])
572 {
573 git_win32_path wpath;
574 int fd, error;
575 DWORD attrs_orig, attrs_new = 0;
576 struct open_opts opts = { 0 };
577
578 if (git_win32_path_from_utf8(wpath, path) < 0)
579 return -1;
580
581 attrs_orig = GetFileAttributesW(wpath);
582
583 if (attrs_orig & FILE_ATTRIBUTE_READONLY) {
584 attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY;
585
586 if (!SetFileAttributesW(wpath, attrs_new)) {
587 git_error_set(GIT_ERROR_OS, "failed to set attributes");
588 return -1;
589 }
590 }
591
592 open_opts_from_posix(&opts, O_RDWR, 0);
593
594 if ((fd = open_once(wpath, &opts)) < 0) {
595 error = -1;
596 goto done;
597 }
598
599 error = p_futimes(fd, times);
600 close(fd);
601
602 done:
603 if (attrs_orig != attrs_new) {
604 DWORD os_error = GetLastError();
605 SetFileAttributesW(wpath, attrs_orig);
606 SetLastError(os_error);
607 }
608
609 return error;
610 }
611
612 int p_futimes(int fd, const struct p_timeval times[2])
613 {
614 HANDLE handle;
615 FILETIME atime = { 0 }, mtime = { 0 };
616
617 if (times == NULL) {
618 SYSTEMTIME st;
619
620 GetSystemTime(&st);
621 SystemTimeToFileTime(&st, &atime);
622 SystemTimeToFileTime(&st, &mtime);
623 }
624 else {
625 git_win32__timeval_to_filetime(&atime, times[0]);
626 git_win32__timeval_to_filetime(&mtime, times[1]);
627 }
628
629 if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
630 return -1;
631
632 if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
633 return -1;
634
635 return 0;
636 }
637
638 int p_getcwd(char *buffer_out, size_t size)
639 {
640 git_win32_path buf;
641 wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
642
643 if (!cwd)
644 return -1;
645
646 /* Convert the working directory back to UTF-8 */
647 if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
648 DWORD code = GetLastError();
649
650 if (code == ERROR_INSUFFICIENT_BUFFER)
651 errno = ERANGE;
652 else
653 errno = EINVAL;
654
655 return -1;
656 }
657
658 return 0;
659 }
660
661 static int getfinalpath_w(
662 git_win32_path dest,
663 const wchar_t *path)
664 {
665 HANDLE hFile;
666 DWORD dwChars;
667
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);
673
674 if (INVALID_HANDLE_VALUE == hFile)
675 return -1;
676
677 /* Call GetFinalPathNameByHandle */
678 dwChars = GetFinalPathNameByHandleW(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
679 CloseHandle(hFile);
680
681 if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
682 return -1;
683
684 /* The path may be delivered to us with a namespace prefix; remove */
685 return (int)git_win32_path_remove_namespace(dest, dwChars);
686 }
687
688 static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
689 {
690 git_win32_path target_w;
691
692 if (getfinalpath_w(target_w, path) < 0)
693 return -1;
694
695 return lstat_w(target_w, buf, false);
696 }
697
698 int p_fstat(int fd, struct stat *buf)
699 {
700 BY_HANDLE_FILE_INFORMATION fhInfo;
701
702 HANDLE fh = (HANDLE)_get_osfhandle(fd);
703
704 if (fh == INVALID_HANDLE_VALUE ||
705 !GetFileInformationByHandle(fh, &fhInfo)) {
706 errno = EBADF;
707 return -1;
708 }
709
710 git_win32__file_information_to_stat(buf, &fhInfo);
711 return 0;
712 }
713
714 int p_stat(const char* path, struct stat* buf)
715 {
716 git_win32_path path_w;
717 int len;
718
719 if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
720 lstat_w(path_w, buf, false) < 0)
721 return -1;
722
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);
727
728 return 0;
729 }
730
731 int p_chdir(const char* path)
732 {
733 git_win32_path buf;
734
735 if (git_win32_path_from_utf8(buf, path) < 0)
736 return -1;
737
738 return _wchdir(buf);
739 }
740
741 int p_chmod(const char* path, mode_t mode)
742 {
743 git_win32_path buf;
744
745 if (git_win32_path_from_utf8(buf, path) < 0)
746 return -1;
747
748 return _wchmod(buf, mode);
749 }
750
751 int p_rmdir(const char* path)
752 {
753 git_win32_path buf;
754 int error;
755
756 if (git_win32_path_from_utf8(buf, path) < 0)
757 return -1;
758
759 error = _wrmdir(buf);
760
761 if (error == -1) {
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.
766 */
767 case ERROR_SHARING_VIOLATION:
768 errno = EBUSY;
769 break;
770
771 /* This error can be returned when trying to rmdir an extant file. */
772 case ERROR_DIRECTORY:
773 errno = ENOTDIR;
774 break;
775 }
776 }
777
778 return error;
779 }
780
781 char *p_realpath(const char *orig_path, char *buffer)
782 {
783 git_win32_path orig_path_w, buffer_w;
784
785 if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
786 return NULL;
787
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;
794 else
795 errno = EINVAL;
796
797 return NULL;
798 }
799
800 /* The path must exist. */
801 if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
802 errno = ENOENT;
803 return NULL;
804 }
805
806 if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
807 errno = ENOMEM;
808 return NULL;
809 }
810
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)
815 return NULL;
816
817 git_path_mkposix(buffer);
818
819 return buffer;
820 }
821
822 int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
823 {
824 #if defined(_MSC_VER)
825 int len;
826
827 if (count == 0)
828 return _vscprintf(format, argptr);
829
830 #if _MSC_VER >= 1500
831 len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
832 #else
833 len = _vsnprintf(buffer, count, format, argptr);
834 #endif
835
836 if (len < 0)
837 return _vscprintf(format, argptr);
838
839 return len;
840 #else /* MinGW */
841 return vsnprintf(buffer, count, format, argptr);
842 #endif
843 }
844
845 int p_snprintf(char *buffer, size_t count, const char *format, ...)
846 {
847 va_list va;
848 int r;
849
850 va_start(va, format);
851 r = p_vsnprintf(buffer, count, format, va);
852 va_end(va);
853
854 return r;
855 }
856
857 /* TODO: wut? */
858 int p_mkstemp(char *tmp_path)
859 {
860 #if defined(_MSC_VER) && _MSC_VER >= 1500
861 if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
862 return -1;
863 #else
864 if (_mktemp(tmp_path) == NULL)
865 return -1;
866 #endif
867
868 return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); /* -V536 */
869 }
870
871 int p_access(const char* path, mode_t mode)
872 {
873 git_win32_path buf;
874
875 if (git_win32_path_from_utf8(buf, path) < 0)
876 return -1;
877
878 return _waccess(buf, mode & WIN32_MODE_MASK);
879 }
880
881 GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to)
882 {
883 if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
884 return 0;
885
886 if (last_error_retryable())
887 return GIT_RETRY;
888
889 set_errno();
890 return -1;
891 }
892
893 int p_rename(const char *from, const char *to)
894 {
895 git_win32_path wfrom, wto;
896
897 if (git_win32_path_from_utf8(wfrom, from) < 0 ||
898 git_win32_path_from_utf8(wto, to) < 0)
899 return -1;
900
901 do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
902 }
903
904 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
905 {
906 if ((size_t)((int)length) != length)
907 return -1; /* git_error_set will be done by caller */
908
909 return recv(socket, buffer, (int)length, flags);
910 }
911
912 int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
913 {
914 if ((size_t)((int)length) != length)
915 return -1; /* git_error_set will be done by caller */
916
917 return send(socket, buffer, (int)length, flags);
918 }
919
920 /**
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
923 */
924 struct tm *
925 p_localtime_r (const time_t *timer, struct tm *result)
926 {
927 struct tm *local_result;
928 local_result = localtime (timer);
929
930 if (local_result == NULL || result == NULL)
931 return NULL;
932
933 memcpy (result, local_result, sizeof (struct tm));
934 return result;
935 }
936 struct tm *
937 p_gmtime_r (const time_t *timer, struct tm *result)
938 {
939 struct tm *local_result;
940 local_result = gmtime (timer);
941
942 if (local_result == NULL || result == NULL)
943 return NULL;
944
945 memcpy (result, local_result, sizeof (struct tm));
946 return result;
947 }
948
949 int p_inet_pton(int af, const char *src, void *dst)
950 {
951 struct sockaddr_storage sin;
952 void *addr;
953 int sin_len = sizeof(struct sockaddr_storage), addr_len;
954 int error = 0;
955
956 if (af == AF_INET) {
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);
962 } else {
963 errno = EAFNOSUPPORT;
964 return -1;
965 }
966
967 if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
968 memcpy(dst, addr, addr_len);
969 return 1;
970 }
971
972 switch(WSAGetLastError()) {
973 case WSAEINVAL:
974 return 0;
975 case WSAEFAULT:
976 errno = ENOSPC;
977 return -1;
978 case WSA_NOT_ENOUGH_MEMORY:
979 errno = ENOMEM;
980 return -1;
981 }
982
983 errno = EINVAL;
984 return -1;
985 }