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