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