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