]> git.proxmox.com Git - libgit2.git/blob - src/win32/posix_w32.c
Merge pull request #3574 from chescock/buffer-sideband-pack-data
[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 /* Options which we always provide to _wopen.
30 *
31 * _O_BINARY - Raw access; no translation of CR or LF characters
32 * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes.
33 * The Windows default is 'not inheritable', but the CRT's default (following
34 * POSIX convention) is 'inheritable'. We have no desire for our handles to be
35 * inheritable on Windows, so specify the flag to get default behavior back. */
36 #define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT)
37
38 /* Allowable mode bits on Win32. Using mode bits that are not supported on
39 * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
40 * so we simply remove them.
41 */
42 #define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
43
44 /* GetFinalPathNameByHandleW signature */
45 typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
46
47 /**
48 * Truncate or extend file.
49 *
50 * We now take a "git_off_t" rather than "long" because
51 * files may be longer than 2Gb.
52 */
53 int p_ftruncate(int fd, git_off_t size)
54 {
55 if (size < 0) {
56 errno = EINVAL;
57 return -1;
58 }
59
60 #if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
61 return ((_chsize_s(fd, size) == 0) ? 0 : -1);
62 #else
63 /* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
64 if (size > INT32_MAX) {
65 errno = EFBIG;
66 return -1;
67 }
68 return _chsize(fd, (long)size);
69 #endif
70 }
71
72 int p_mkdir(const char *path, mode_t mode)
73 {
74 git_win32_path buf;
75
76 GIT_UNUSED(mode);
77
78 if (git_win32_path_from_utf8(buf, path) < 0)
79 return -1;
80
81 return _wmkdir(buf);
82 }
83
84 int 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
92 int p_unlink(const char *path)
93 {
94 git_win32_path buf;
95 int error;
96
97 if (git_win32_path_from_utf8(buf, path) < 0)
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 */
104 if (error == -1 && errno == EACCES) {
105 _wchmod(buf, 0666);
106 error = _wunlink(buf);
107 }
108
109 return error;
110 }
111
112 int 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
135 #define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
136
137 static int lstat_w(
138 wchar_t *path,
139 struct stat *buf,
140 bool posix_enotdir)
141 {
142 WIN32_FILE_ATTRIBUTE_DATA fdata;
143
144 if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
145 if (!buf)
146 return 0;
147
148 return git_win32__file_attribute_to_stat(buf, &fdata, path);
149 }
150
151 switch (GetLastError()) {
152 case ERROR_ACCESS_DENIED:
153 errno = EACCES;
154 break;
155 default:
156 errno = ENOENT;
157 break;
158 }
159
160 /* To match POSIX behavior, set ENOTDIR when any of the folders in the
161 * file path is a regular file, otherwise set ENOENT.
162 */
163 if (errno == ENOENT && posix_enotdir) {
164 size_t path_len = wcslen(path);
165
166 /* scan up path until we find an existing item */
167 while (1) {
168 DWORD attrs;
169
170 /* remove last directory component */
171 for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
172
173 if (path_len <= 0)
174 break;
175
176 path[path_len] = L'\0';
177 attrs = GetFileAttributesW(path);
178
179 if (attrs != INVALID_FILE_ATTRIBUTES) {
180 if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
181 errno = ENOTDIR;
182 break;
183 }
184 }
185 }
186
187 return -1;
188 }
189
190 static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
191 {
192 git_win32_path path_w;
193 int len;
194
195 if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
196 return -1;
197
198 git_win32__path_trim_end(path_w, len);
199
200 return lstat_w(path_w, buf, posixly_correct);
201 }
202
203 int p_lstat(const char *filename, struct stat *buf)
204 {
205 return do_lstat(filename, buf, false);
206 }
207
208 int p_lstat_posixly(const char *filename, struct stat *buf)
209 {
210 return do_lstat(filename, buf, true);
211 }
212
213 int p_utimes(const char *filename, const struct p_timeval times[2])
214 {
215 int fd, error;
216
217 if ((fd = p_open(filename, O_RDWR)) < 0)
218 return fd;
219
220 error = p_futimes(fd, times);
221
222 close(fd);
223 return error;
224 }
225
226 int p_futimes(int fd, const struct p_timeval times[2])
227 {
228 HANDLE handle;
229 FILETIME atime = {0}, mtime = {0};
230
231 if (times == NULL) {
232 SYSTEMTIME st;
233
234 GetSystemTime(&st);
235 SystemTimeToFileTime(&st, &atime);
236 SystemTimeToFileTime(&st, &mtime);
237 } else {
238 git_win32__timeval_to_filetime(&atime, times[0]);
239 git_win32__timeval_to_filetime(&mtime, times[1]);
240 }
241
242 if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
243 return -1;
244
245 if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
246 return -1;
247
248 return 0;
249 }
250
251 int p_readlink(const char *path, char *buf, size_t bufsiz)
252 {
253 git_win32_path path_w, target_w;
254 git_win32_utf8_path target;
255 int len;
256
257 /* readlink(2) does not NULL-terminate the string written
258 * to the target buffer. Furthermore, the target buffer need
259 * not be large enough to hold the entire result. A truncated
260 * result should be written in this case. Since this truncation
261 * could occur in the middle of the encoding of a code point,
262 * we need to buffer the result on the stack. */
263
264 if (git_win32_path_from_utf8(path_w, path) < 0 ||
265 git_win32_path_readlink_w(target_w, path_w) < 0 ||
266 (len = git_win32_path_to_utf8(target, target_w)) < 0)
267 return -1;
268
269 bufsiz = min((size_t)len, bufsiz);
270 memcpy(buf, target, bufsiz);
271
272 return (int)bufsiz;
273 }
274
275 int p_symlink(const char *old, const char *new)
276 {
277 /* Real symlinks on NTFS require admin privileges. Until this changes,
278 * libgit2 just creates a text file with the link target in the contents.
279 */
280 return git_futils_fake_symlink(old, new);
281 }
282
283 int p_open(const char *path, int flags, ...)
284 {
285 git_win32_path buf;
286 mode_t mode = 0;
287
288 if (git_win32_path_from_utf8(buf, path) < 0)
289 return -1;
290
291 if (flags & O_CREAT) {
292 va_list arg_list;
293
294 va_start(arg_list, flags);
295 mode = (mode_t)va_arg(arg_list, int);
296 va_end(arg_list);
297 }
298
299 return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode & WIN32_MODE_MASK);
300 }
301
302 int p_creat(const char *path, mode_t mode)
303 {
304 git_win32_path buf;
305
306 if (git_win32_path_from_utf8(buf, path) < 0)
307 return -1;
308
309 return _wopen(buf,
310 _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS,
311 mode & WIN32_MODE_MASK);
312 }
313
314 int p_getcwd(char *buffer_out, size_t size)
315 {
316 git_win32_path buf;
317 wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
318
319 if (!cwd)
320 return -1;
321
322 /* Convert the working directory back to UTF-8 */
323 if (git__utf16_to_8(buffer_out, size, cwd) < 0) {
324 DWORD code = GetLastError();
325
326 if (code == ERROR_INSUFFICIENT_BUFFER)
327 errno = ERANGE;
328 else
329 errno = EINVAL;
330
331 return -1;
332 }
333
334 return 0;
335 }
336
337 /*
338 * Returns the address of the GetFinalPathNameByHandleW function.
339 * This function is available on Windows Vista and higher.
340 */
341 static PFGetFinalPathNameByHandleW get_fpnbyhandle(void)
342 {
343 static PFGetFinalPathNameByHandleW pFunc = NULL;
344 PFGetFinalPathNameByHandleW toReturn = pFunc;
345
346 if (!toReturn) {
347 HMODULE hModule = GetModuleHandleW(L"kernel32");
348
349 if (hModule)
350 toReturn = (PFGetFinalPathNameByHandleW)GetProcAddress(hModule, "GetFinalPathNameByHandleW");
351
352 pFunc = toReturn;
353 }
354
355 assert(toReturn);
356
357 return toReturn;
358 }
359
360 static int getfinalpath_w(
361 git_win32_path dest,
362 const wchar_t *path)
363 {
364 PFGetFinalPathNameByHandleW pgfp = get_fpnbyhandle();
365 HANDLE hFile;
366 DWORD dwChars;
367
368 if (!pgfp)
369 return -1;
370
371 /* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
372 * specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
373 * target of the link. */
374 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
375 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
376
377 if (INVALID_HANDLE_VALUE == hFile)
378 return -1;
379
380 /* Call GetFinalPathNameByHandle */
381 dwChars = pgfp(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
382 CloseHandle(hFile);
383
384 if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
385 return -1;
386
387 /* The path may be delivered to us with a prefix; canonicalize */
388 return (int)git_win32__canonicalize_path(dest, dwChars);
389 }
390
391 static int follow_and_lstat_link(git_win32_path path, struct stat* buf)
392 {
393 git_win32_path target_w;
394
395 if (getfinalpath_w(target_w, path) < 0)
396 return -1;
397
398 return lstat_w(target_w, buf, false);
399 }
400
401 int p_fstat(int fd, struct stat *buf)
402 {
403 BY_HANDLE_FILE_INFORMATION fhInfo;
404
405 HANDLE fh = (HANDLE)_get_osfhandle(fd);
406
407 if (fh == INVALID_HANDLE_VALUE ||
408 !GetFileInformationByHandle(fh, &fhInfo)) {
409 errno = EBADF;
410 return -1;
411 }
412
413 git_win32__file_information_to_stat(buf, &fhInfo);
414 return 0;
415 }
416
417 int p_stat(const char* path, struct stat* buf)
418 {
419 git_win32_path path_w;
420 int len;
421
422 if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
423 lstat_w(path_w, buf, false) < 0)
424 return -1;
425
426 /* The item is a symbolic link or mount point. No need to iterate
427 * to follow multiple links; use GetFinalPathNameFromHandle. */
428 if (S_ISLNK(buf->st_mode))
429 return follow_and_lstat_link(path_w, buf);
430
431 return 0;
432 }
433
434 int p_chdir(const char* path)
435 {
436 git_win32_path buf;
437
438 if (git_win32_path_from_utf8(buf, path) < 0)
439 return -1;
440
441 return _wchdir(buf);
442 }
443
444 int p_chmod(const char* path, mode_t mode)
445 {
446 git_win32_path buf;
447
448 if (git_win32_path_from_utf8(buf, path) < 0)
449 return -1;
450
451 return _wchmod(buf, mode);
452 }
453
454 int p_rmdir(const char* path)
455 {
456 git_win32_path buf;
457 int error;
458
459 if (git_win32_path_from_utf8(buf, path) < 0)
460 return -1;
461
462 error = _wrmdir(buf);
463
464 if (error == -1) {
465 switch (GetLastError()) {
466 /* _wrmdir() is documented to return EACCES if "A program has an open
467 * handle to the directory." This sounds like what everybody else calls
468 * EBUSY. Let's convert appropriate error codes.
469 */
470 case ERROR_SHARING_VIOLATION:
471 errno = EBUSY;
472 break;
473
474 /* This error can be returned when trying to rmdir an extant file. */
475 case ERROR_DIRECTORY:
476 errno = ENOTDIR;
477 break;
478 }
479 }
480
481 return error;
482 }
483
484 char *p_realpath(const char *orig_path, char *buffer)
485 {
486 git_win32_path orig_path_w, buffer_w;
487
488 if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
489 return NULL;
490
491 /* Note that if the path provided is a relative path, then the current directory
492 * is used to resolve the path -- which is a concurrency issue because the current
493 * directory is a process-wide variable. */
494 if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
495 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
496 errno = ENAMETOOLONG;
497 else
498 errno = EINVAL;
499
500 return NULL;
501 }
502
503 /* The path must exist. */
504 if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
505 errno = ENOENT;
506 return NULL;
507 }
508
509 if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
510 errno = ENOMEM;
511 return NULL;
512 }
513
514 /* Convert the path to UTF-8. If the caller provided a buffer, then it
515 * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
516 * then we may overflow. */
517 if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
518 return NULL;
519
520 git_path_mkposix(buffer);
521
522 return buffer;
523 }
524
525 int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
526 {
527 #if defined(_MSC_VER)
528 int len;
529
530 if (count == 0)
531 return _vscprintf(format, argptr);
532
533 #if _MSC_VER >= 1500
534 len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
535 #else
536 len = _vsnprintf(buffer, count, format, argptr);
537 #endif
538
539 if (len < 0)
540 return _vscprintf(format, argptr);
541
542 return len;
543 #else /* MinGW */
544 return vsnprintf(buffer, count, format, argptr);
545 #endif
546 }
547
548 int p_snprintf(char *buffer, size_t count, const char *format, ...)
549 {
550 va_list va;
551 int r;
552
553 va_start(va, format);
554 r = p_vsnprintf(buffer, count, format, va);
555 va_end(va);
556
557 return r;
558 }
559
560 /* TODO: wut? */
561 int p_mkstemp(char *tmp_path)
562 {
563 #if defined(_MSC_VER) && _MSC_VER >= 1500
564 if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
565 return -1;
566 #else
567 if (_mktemp(tmp_path) == NULL)
568 return -1;
569 #endif
570
571 return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536
572 }
573
574 int p_access(const char* path, mode_t mode)
575 {
576 git_win32_path buf;
577
578 if (git_win32_path_from_utf8(buf, path) < 0)
579 return -1;
580
581 return _waccess(buf, mode & WIN32_MODE_MASK);
582 }
583
584 static int ensure_writable(wchar_t *fpath)
585 {
586 DWORD attrs;
587
588 attrs = GetFileAttributesW(fpath);
589 if (attrs == INVALID_FILE_ATTRIBUTES) {
590 if (GetLastError() == ERROR_FILE_NOT_FOUND)
591 return 0;
592
593 giterr_set(GITERR_OS, "failed to get attributes");
594 return -1;
595 }
596
597 if (!(attrs & FILE_ATTRIBUTE_READONLY))
598 return 0;
599
600 attrs &= ~FILE_ATTRIBUTE_READONLY;
601 if (!SetFileAttributesW(fpath, attrs)) {
602 giterr_set(GITERR_OS, "failed to set attributes");
603 return -1;
604 }
605
606 return 0;
607 }
608
609 int p_rename(const char *from, const char *to)
610 {
611 git_win32_path wfrom;
612 git_win32_path wto;
613 int rename_tries;
614 int rename_succeeded;
615 int error;
616
617 if (git_win32_path_from_utf8(wfrom, from) < 0 ||
618 git_win32_path_from_utf8(wto, to) < 0)
619 return -1;
620
621 /* wait up to 50ms if file is locked by another thread or process */
622 rename_tries = 0;
623 rename_succeeded = 0;
624 while (rename_tries < 10) {
625 if (ensure_writable(wto) == 0 &&
626 MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) {
627 rename_succeeded = 1;
628 break;
629 }
630
631 error = GetLastError();
632 if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) {
633 Sleep(5);
634 rename_tries++;
635 } else
636 break;
637 }
638
639 return rename_succeeded ? 0 : -1;
640 }
641
642 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
643 {
644 if ((size_t)((int)length) != length)
645 return -1; /* giterr_set will be done by caller */
646
647 return recv(socket, buffer, (int)length, flags);
648 }
649
650 int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
651 {
652 if ((size_t)((int)length) != length)
653 return -1; /* giterr_set will be done by caller */
654
655 return send(socket, buffer, (int)length, flags);
656 }
657
658 /**
659 * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
660 * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
661 */
662 struct tm *
663 p_localtime_r (const time_t *timer, struct tm *result)
664 {
665 struct tm *local_result;
666 local_result = localtime (timer);
667
668 if (local_result == NULL || result == NULL)
669 return NULL;
670
671 memcpy (result, local_result, sizeof (struct tm));
672 return result;
673 }
674 struct tm *
675 p_gmtime_r (const time_t *timer, struct tm *result)
676 {
677 struct tm *local_result;
678 local_result = gmtime (timer);
679
680 if (local_result == NULL || result == NULL)
681 return NULL;
682
683 memcpy (result, local_result, sizeof (struct tm));
684 return result;
685 }
686
687 int p_inet_pton(int af, const char *src, void *dst)
688 {
689 struct sockaddr_storage sin;
690 void *addr;
691 int sin_len = sizeof(struct sockaddr_storage), addr_len;
692 int error = 0;
693
694 if (af == AF_INET) {
695 addr = &((struct sockaddr_in *)&sin)->sin_addr;
696 addr_len = sizeof(struct in_addr);
697 } else if (af == AF_INET6) {
698 addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
699 addr_len = sizeof(struct in6_addr);
700 } else {
701 errno = EAFNOSUPPORT;
702 return -1;
703 }
704
705 if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
706 memcpy(dst, addr, addr_len);
707 return 1;
708 }
709
710 switch(WSAGetLastError()) {
711 case WSAEINVAL:
712 return 0;
713 case WSAEFAULT:
714 errno = ENOSPC;
715 return -1;
716 case WSA_NOT_ENOUGH_MEMORY:
717 errno = ENOMEM;
718 return -1;
719 }
720
721 errno = EINVAL;
722 return -1;
723 }